summaryrefslogtreecommitdiffstats
path: root/dom/media/platforms/omx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /dom/media/platforms/omx
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/media/platforms/omx')
-rw-r--r--dom/media/platforms/omx/OmxCoreLibLinker.cpp113
-rw-r--r--dom/media/platforms/omx/OmxCoreLibLinker.h36
-rw-r--r--dom/media/platforms/omx/OmxDataDecoder.cpp1012
-rw-r--r--dom/media/platforms/omx/OmxDataDecoder.h224
-rw-r--r--dom/media/platforms/omx/OmxDecoderModule.cpp59
-rw-r--r--dom/media/platforms/omx/OmxDecoderModule.h33
-rw-r--r--dom/media/platforms/omx/OmxFunctionList.h13
-rw-r--r--dom/media/platforms/omx/OmxPlatformLayer.cpp307
-rw-r--r--dom/media/platforms/omx/OmxPlatformLayer.h103
-rw-r--r--dom/media/platforms/omx/OmxPromiseLayer.cpp355
-rw-r--r--dom/media/platforms/omx/OmxPromiseLayer.h243
-rw-r--r--dom/media/platforms/omx/PureOmxPlatformLayer.cpp405
-rw-r--r--dom/media/platforms/omx/PureOmxPlatformLayer.h110
-rw-r--r--dom/media/platforms/omx/moz.build36
14 files changed, 3049 insertions, 0 deletions
diff --git a/dom/media/platforms/omx/OmxCoreLibLinker.cpp b/dom/media/platforms/omx/OmxCoreLibLinker.cpp
new file mode 100644
index 0000000000..a0ee61ec42
--- /dev/null
+++ b/dom/media/platforms/omx/OmxCoreLibLinker.cpp
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OmxCoreLibLinker.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Preferences.h"
+#include "MainThreadUtils.h"
+#include "prlink.h"
+#include "PlatformDecoderModule.h"
+
+#ifdef LOG
+# undef LOG
+#endif
+
+#define LOG(arg, ...) \
+ MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, \
+ ("OmxCoreLibLinker::%s: " arg, __func__, ##__VA_ARGS__))
+
+namespace mozilla {
+
+OmxCoreLibLinker::LinkStatus OmxCoreLibLinker::sLinkStatus = LinkStatus_INIT;
+
+const char* OmxCoreLibLinker::sLibNames[] = {
+ "libopenmaxil.so", // Raspberry Pi
+ "libomxr_core.so", // Renesas R-Car, RZ/G
+ "libomxil-bellagio.so.0", // Bellagio: An OSS implementation of OpenMAX IL
+};
+
+PRLibrary* OmxCoreLibLinker::sLinkedLib = nullptr;
+const char* OmxCoreLibLinker::sLibName = nullptr;
+
+#define OMX_FUNC(func) void (*func)();
+#include "OmxFunctionList.h"
+#undef OMX_FUNC
+
+bool OmxCoreLibLinker::TryLinkingLibrary(const char* libName) {
+ PRLibSpec lspec;
+ lspec.type = PR_LibSpec_Pathname;
+ lspec.value.pathname = libName;
+ sLinkedLib = PR_LoadLibraryWithFlags(lspec, PR_LD_NOW | PR_LD_LOCAL);
+ if (sLinkedLib) {
+ if (Bind(libName)) {
+ sLibName = libName;
+ LOG("Succeeded to load %s", libName);
+ return true;
+ } else {
+ LOG("Failed to link %s", libName);
+ }
+ Unlink();
+ }
+ return false;
+}
+
+/* static */
+bool OmxCoreLibLinker::Link() {
+ LOG("");
+
+ if (sLinkStatus) {
+ return sLinkStatus == LinkStatus_SUCCEEDED;
+ }
+
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsAutoCString libPath;
+ nsresult rv = Preferences::GetCString("media.omx.core-lib-path", libPath);
+ if (NS_SUCCEEDED(rv) && !libPath.IsEmpty()) {
+ if (TryLinkingLibrary(libPath.Data())) {
+ sLinkStatus = LinkStatus_SUCCEEDED;
+ return true;
+ }
+ }
+
+ // try known paths
+ for (size_t i = 0; i < ArrayLength(sLibNames); i++) {
+ if (TryLinkingLibrary(sLibNames[i])) {
+ sLinkStatus = LinkStatus_SUCCEEDED;
+ return true;
+ }
+ }
+ sLinkStatus = LinkStatus_FAILED;
+ return false;
+}
+
+/* static */
+bool OmxCoreLibLinker::Bind(const char* aLibName) {
+#define OMX_FUNC(func) \
+ { \
+ if (!(func = (typeof(func))PR_FindSymbol(sLinkedLib, #func))) { \
+ LOG("Couldn't load function " #func " from %s.", aLibName); \
+ return false; \
+ } \
+ }
+#include "OmxFunctionList.h"
+#undef OMX_FUNC
+ return true;
+}
+
+/* static */
+void OmxCoreLibLinker::Unlink() {
+ LOG("");
+
+ if (sLinkedLib) {
+ PR_UnloadLibrary(sLinkedLib);
+ sLinkedLib = nullptr;
+ sLibName = nullptr;
+ sLinkStatus = LinkStatus_INIT;
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/media/platforms/omx/OmxCoreLibLinker.h b/dom/media/platforms/omx/OmxCoreLibLinker.h
new file mode 100644
index 0000000000..aaf1bf92de
--- /dev/null
+++ b/dom/media/platforms/omx/OmxCoreLibLinker.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __OmxCoreLibLinker_h__
+#define __OmxCoreLibLinker_h__
+
+struct PRLibrary;
+
+namespace mozilla {
+
+class OmxCoreLibLinker {
+ public:
+ static bool Link();
+ static void Unlink();
+
+ private:
+ static PRLibrary* sLinkedLib;
+ static const char* sLibName;
+ static const char* sLibNames[];
+
+ static bool TryLinkingLibrary(const char* libName);
+ static bool Bind(const char* aLibName);
+
+ static enum LinkStatus {
+ LinkStatus_INIT = 0,
+ LinkStatus_FAILED,
+ LinkStatus_SUCCEEDED
+ } sLinkStatus;
+};
+
+} // namespace mozilla
+
+#endif // __OmxCoreLibLinker_h__
diff --git a/dom/media/platforms/omx/OmxDataDecoder.cpp b/dom/media/platforms/omx/OmxDataDecoder.cpp
new file mode 100644
index 0000000000..e830f77dd2
--- /dev/null
+++ b/dom/media/platforms/omx/OmxDataDecoder.cpp
@@ -0,0 +1,1012 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OmxDataDecoder.h"
+
+#include "OMX_Audio.h"
+#include "OMX_Component.h"
+#include "OMX_Types.h"
+#include "OmxPlatformLayer.h"
+#include "mozilla/IntegerPrintfMacros.h"
+
+#ifdef LOG
+# undef LOG
+# undef LOGL
+#endif
+
+#define LOG(arg, ...) \
+ DDMOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, "::%s: " arg, __func__, \
+ ##__VA_ARGS__)
+
+#define LOGL(arg, ...) \
+ DDMOZ_LOGEX(self.get(), sPDMLog, mozilla::LogLevel::Debug, "::%s: " arg, \
+ __func__, ##__VA_ARGS__)
+
+#define CHECK_OMX_ERR(err) \
+ if (err != OMX_ErrorNone) { \
+ NotifyError(err, __func__); \
+ return; \
+ }
+
+namespace mozilla {
+
+using namespace gfx;
+
+static const char* StateTypeToStr(OMX_STATETYPE aType) {
+ MOZ_ASSERT(aType == OMX_StateLoaded || aType == OMX_StateIdle ||
+ aType == OMX_StateExecuting || aType == OMX_StatePause ||
+ aType == OMX_StateWaitForResources || aType == OMX_StateInvalid);
+
+ switch (aType) {
+ case OMX_StateLoaded:
+ return "OMX_StateLoaded";
+ case OMX_StateIdle:
+ return "OMX_StateIdle";
+ case OMX_StateExecuting:
+ return "OMX_StateExecuting";
+ case OMX_StatePause:
+ return "OMX_StatePause";
+ case OMX_StateWaitForResources:
+ return "OMX_StateWaitForResources";
+ case OMX_StateInvalid:
+ return "OMX_StateInvalid";
+ default:
+ return "Unknown";
+ }
+}
+
+// A helper class to retrieve AudioData or VideoData.
+class MediaDataHelper {
+ protected:
+ virtual ~MediaDataHelper() = default;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDataHelper)
+
+ MediaDataHelper(const TrackInfo* aTrackInfo,
+ layers::ImageContainer* aImageContainer,
+ OmxPromiseLayer* aOmxLayer);
+
+ already_AddRefed<MediaData> GetMediaData(BufferData* aBufferData,
+ bool& aPlatformDepenentData);
+
+ protected:
+ already_AddRefed<AudioData> CreateAudioData(BufferData* aBufferData);
+
+ already_AddRefed<VideoData> CreateYUV420VideoData(BufferData* aBufferData);
+
+ const TrackInfo* mTrackInfo;
+
+ OMX_PARAM_PORTDEFINITIONTYPE mOutputPortDef;
+
+ // audio output
+ MediaQueue<AudioData> mAudioQueue;
+
+ AudioCompactor mAudioCompactor;
+
+ // video output
+ RefPtr<layers::ImageContainer> mImageContainer;
+};
+
+OmxDataDecoder::OmxDataDecoder(const TrackInfo& aTrackInfo,
+ layers::ImageContainer* aImageContainer,
+ Maybe<TrackingId> aTrackingId)
+ : mOmxTaskQueue(
+ CreateMediaDecodeTaskQueue("OmxDataDecoder::mOmxTaskQueue")),
+ mImageContainer(aImageContainer),
+ mWatchManager(this, mOmxTaskQueue),
+ mOmxState(OMX_STATETYPE::OMX_StateInvalid, "OmxDataDecoder::mOmxState"),
+ mTrackInfo(aTrackInfo.Clone()),
+ mFlushing(false),
+ mShuttingDown(false),
+ mCheckingInputExhausted(false),
+ mPortSettingsChanged(-1, "OmxDataDecoder::mPortSettingsChanged"),
+ mTrackingId(std::move(aTrackingId)) {
+ LOG("");
+ mOmxLayer = new OmxPromiseLayer(mOmxTaskQueue, this, aImageContainer);
+}
+
+OmxDataDecoder::~OmxDataDecoder() { LOG(""); }
+
+void OmxDataDecoder::InitializationTask() {
+ mWatchManager.Watch(mOmxState, &OmxDataDecoder::OmxStateRunner);
+ mWatchManager.Watch(mPortSettingsChanged,
+ &OmxDataDecoder::PortSettingsChanged);
+}
+
+void OmxDataDecoder::EndOfStream() {
+ LOG("");
+ MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+ RefPtr<OmxDataDecoder> self = this;
+ mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
+ ->Then(mOmxTaskQueue, __func__,
+ [self, this](OmxCommandPromise::ResolveOrRejectValue&& aValue) {
+ mDrainPromise.ResolveIfExists(std::move(mDecodedData), __func__);
+ mDecodedData = DecodedData();
+ });
+}
+
+RefPtr<MediaDataDecoder::InitPromise> OmxDataDecoder::Init() {
+ LOG("");
+
+ mThread = GetCurrentSerialEventTarget();
+ RefPtr<OmxDataDecoder> self = this;
+ return InvokeAsync(mOmxTaskQueue, __func__, [self, this]() {
+ InitializationTask();
+
+ RefPtr<InitPromise> p = mInitPromise.Ensure(__func__);
+ mOmxLayer->Init(mTrackInfo.get())
+ ->Then(
+ mOmxTaskQueue, __func__,
+ [self, this]() {
+ // Omx state should be OMX_StateIdle.
+ mOmxState = mOmxLayer->GetState();
+ MOZ_ASSERT(mOmxState != OMX_StateIdle);
+ },
+ [self, this]() {
+ RejectInitPromise(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
+ });
+ return p;
+ });
+}
+
+RefPtr<MediaDataDecoder::DecodePromise> OmxDataDecoder::Decode(
+ MediaRawData* aSample) {
+ LOG("sample %p", aSample);
+ MOZ_ASSERT(mThread->IsOnCurrentThread());
+ MOZ_ASSERT(mInitPromise.IsEmpty());
+
+ RefPtr<OmxDataDecoder> self = this;
+ RefPtr<MediaRawData> sample = aSample;
+ return InvokeAsync(mOmxTaskQueue, __func__, [self, this, sample]() {
+ RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
+
+ mTrackingId.apply([&](const auto& aId) {
+ MediaInfoFlag flag = MediaInfoFlag::None;
+ flag |= (sample->mKeyframe ? MediaInfoFlag::KeyFrame
+ : MediaInfoFlag::NonKeyFrame);
+
+ mPerformanceRecorder.Start(sample->mTimecode.ToMicroseconds(),
+ "OmxDataDecoder"_ns, aId, flag);
+ });
+ mMediaRawDatas.AppendElement(std::move(sample));
+
+ // Start to fill/empty buffers.
+ if (mOmxState == OMX_StateIdle || mOmxState == OMX_StateExecuting) {
+ FillAndEmptyBuffers();
+ }
+ return p;
+ });
+}
+
+RefPtr<MediaDataDecoder::FlushPromise> OmxDataDecoder::Flush() {
+ LOG("");
+ MOZ_ASSERT(mThread->IsOnCurrentThread());
+
+ mFlushing = true;
+
+ return InvokeAsync(mOmxTaskQueue, this, __func__, &OmxDataDecoder::DoFlush);
+}
+
+RefPtr<MediaDataDecoder::DecodePromise> OmxDataDecoder::Drain() {
+ LOG("");
+ MOZ_ASSERT(mThread->IsOnCurrentThread());
+
+ RefPtr<OmxDataDecoder> self = this;
+ return InvokeAsync(mOmxTaskQueue, __func__, [self]() {
+ RefPtr<DecodePromise> p = self->mDrainPromise.Ensure(__func__);
+ self->SendEosBuffer();
+ return p;
+ });
+}
+
+RefPtr<ShutdownPromise> OmxDataDecoder::Shutdown() {
+ LOG("");
+ // mThread may not be set if Init hasn't been called first.
+ MOZ_ASSERT(!mThread || mThread->IsOnCurrentThread());
+
+ mShuttingDown = true;
+
+ return InvokeAsync(mOmxTaskQueue, this, __func__,
+ &OmxDataDecoder::DoAsyncShutdown);
+}
+
+RefPtr<ShutdownPromise> OmxDataDecoder::DoAsyncShutdown() {
+ LOG("");
+ MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+ MOZ_ASSERT(!mFlushing);
+
+ mWatchManager.Unwatch(mOmxState, &OmxDataDecoder::OmxStateRunner);
+ mWatchManager.Unwatch(mPortSettingsChanged,
+ &OmxDataDecoder::PortSettingsChanged);
+
+ // Flush to all ports, so all buffers can be returned from component.
+ RefPtr<OmxDataDecoder> self = this;
+ mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
+ ->Then(
+ mOmxTaskQueue, __func__,
+ [self]() -> RefPtr<OmxCommandPromise> {
+ LOGL("DoAsyncShutdown: flush complete");
+ return self->mOmxLayer->SendCommand(OMX_CommandStateSet,
+ OMX_StateIdle, nullptr);
+ },
+ [self](const OmxCommandFailureHolder& aError) {
+ self->mOmxLayer->Shutdown();
+ return OmxCommandPromise::CreateAndReject(aError, __func__);
+ })
+ ->Then(
+ mOmxTaskQueue, __func__,
+ [self]() -> RefPtr<OmxCommandPromise> {
+ RefPtr<OmxCommandPromise> p = self->mOmxLayer->SendCommand(
+ OMX_CommandStateSet, OMX_StateLoaded, nullptr);
+
+ // According to spec 3.1.1.2.2.1:
+ // OMX_StateLoaded needs to be sent before releasing buffers.
+ // And state transition from OMX_StateIdle to OMX_StateLoaded
+ // is completed when all of the buffers have been removed
+ // from the component.
+ // Here the buffer promises are not resolved due to displaying
+ // in layer, it needs to wait before the layer returns the
+ // buffers.
+ LOGL("DoAsyncShutdown: releasing buffers...");
+ self->ReleaseBuffers(OMX_DirInput);
+ self->ReleaseBuffers(OMX_DirOutput);
+
+ return p;
+ },
+ [self](const OmxCommandFailureHolder& aError) {
+ self->mOmxLayer->Shutdown();
+ return OmxCommandPromise::CreateAndReject(aError, __func__);
+ })
+ ->Then(
+ mOmxTaskQueue, __func__,
+ [self]() -> RefPtr<ShutdownPromise> {
+ LOGL(
+ "DoAsyncShutdown: OMX_StateLoaded, it is safe to shutdown omx");
+ self->mOmxLayer->Shutdown();
+ self->mWatchManager.Shutdown();
+ self->mOmxLayer = nullptr;
+ self->mMediaDataHelper = nullptr;
+ self->mShuttingDown = false;
+ return ShutdownPromise::CreateAndResolve(true, __func__);
+ },
+ [self]() -> RefPtr<ShutdownPromise> {
+ self->mOmxLayer->Shutdown();
+ self->mWatchManager.Shutdown();
+ self->mOmxLayer = nullptr;
+ self->mMediaDataHelper = nullptr;
+ return ShutdownPromise::CreateAndReject(false, __func__);
+ })
+ ->Then(
+ mThread, __func__,
+ [self]() {
+ self->mOmxTaskQueue->BeginShutdown();
+ self->mOmxTaskQueue->AwaitShutdownAndIdle();
+ self->mShutdownPromise.Resolve(true, __func__);
+ },
+ [self]() {
+ self->mOmxTaskQueue->BeginShutdown();
+ self->mOmxTaskQueue->AwaitShutdownAndIdle();
+ self->mShutdownPromise.Resolve(true, __func__);
+ });
+ return mShutdownPromise.Ensure(__func__);
+}
+
+void OmxDataDecoder::FillBufferDone(BufferData* aData) {
+ MOZ_ASSERT(!aData || aData->mStatus == BufferData::BufferStatus::OMX_CLIENT);
+
+ // Don't output sample when flush or shutting down, especially for video
+ // decoded frame. Because video decoded frame can have a promise in
+ // BufferData waiting for layer to resolve it via recycle callback, if other
+ // module doesn't send it to layer, it will cause a unresolved promise and
+ // waiting for resolve infinitely.
+ if (mFlushing || mShuttingDown) {
+ LOG("mFlush or mShuttingDown, drop data");
+ aData->mStatus = BufferData::BufferStatus::FREE;
+ return;
+ }
+
+ if (aData->mBuffer->nFlags & OMX_BUFFERFLAG_EOS) {
+ // Reach eos, it's an empty data so it doesn't need to output.
+ EndOfStream();
+ aData->mStatus = BufferData::BufferStatus::FREE;
+ } else {
+ Output(aData);
+ FillAndEmptyBuffers();
+ }
+}
+
+void OmxDataDecoder::Output(BufferData* aData) {
+ if (!mMediaDataHelper) {
+ mMediaDataHelper =
+ new MediaDataHelper(mTrackInfo.get(), mImageContainer, mOmxLayer);
+ }
+
+ bool isPlatformData = false;
+ RefPtr<MediaData> data =
+ mMediaDataHelper->GetMediaData(aData, isPlatformData);
+ if (!data) {
+ aData->mStatus = BufferData::BufferStatus::FREE;
+ return;
+ }
+
+ if (isPlatformData) {
+ // If the MediaData is platform dependnet data, it's mostly a kind of
+ // limited resource, so we use promise to notify when the resource is free.
+ aData->mStatus = BufferData::BufferStatus::OMX_CLIENT_OUTPUT;
+
+ MOZ_RELEASE_ASSERT(aData->mPromise.IsEmpty());
+ RefPtr<OmxBufferPromise> p = aData->mPromise.Ensure(__func__);
+
+ RefPtr<OmxDataDecoder> self = this;
+ RefPtr<BufferData> buffer = aData;
+ p->Then(
+ mOmxTaskQueue, __func__,
+ [self, buffer]() {
+ MOZ_RELEASE_ASSERT(buffer->mStatus ==
+ BufferData::BufferStatus::OMX_CLIENT_OUTPUT);
+ buffer->mStatus = BufferData::BufferStatus::FREE;
+ self->FillAndEmptyBuffers();
+ },
+ [buffer]() {
+ MOZ_RELEASE_ASSERT(buffer->mStatus ==
+ BufferData::BufferStatus::OMX_CLIENT_OUTPUT);
+ buffer->mStatus = BufferData::BufferStatus::FREE;
+ });
+ } else {
+ aData->mStatus = BufferData::BufferStatus::FREE;
+ }
+
+ if (mTrackInfo->IsVideo()) {
+ mPerformanceRecorder.Record(
+ aData->mRawData->mTimecode.ToMicroseconds(), [&](DecodeStage& aStage) {
+ const auto& image = data->As<VideoData>()->mImage;
+ aStage.SetResolution(image->GetSize().Width(),
+ image->GetSize().Height());
+ aStage.SetImageFormat(DecodeStage::YUV420P);
+ aStage.SetColorDepth(image->GetColorDepth());
+ });
+ }
+
+ mDecodedData.AppendElement(std::move(data));
+}
+
+void OmxDataDecoder::FillBufferFailure(OmxBufferFailureHolder aFailureHolder) {
+ NotifyError(aFailureHolder.mError, __func__);
+}
+
+void OmxDataDecoder::EmptyBufferDone(BufferData* aData) {
+ MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+ MOZ_ASSERT(!aData || aData->mStatus == BufferData::BufferStatus::OMX_CLIENT);
+
+ // Nothing to do when status of input buffer is OMX_CLIENT.
+ aData->mStatus = BufferData::BufferStatus::FREE;
+ FillAndEmptyBuffers();
+
+ // There is no way to know if component gets enough raw samples to generate
+ // output, especially for video decoding. So here it needs to request raw
+ // samples aggressively.
+ if (!mCheckingInputExhausted && !mMediaRawDatas.Length()) {
+ mCheckingInputExhausted = true;
+
+ RefPtr<OmxDataDecoder> self = this;
+ nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+ "OmxDataDecoder::EmptyBufferDone", [self, this]() {
+ mCheckingInputExhausted = false;
+
+ if (mMediaRawDatas.Length()) {
+ return;
+ }
+
+ mDecodePromise.ResolveIfExists(std::move(mDecodedData), __func__);
+ mDecodedData = DecodedData();
+ });
+
+ nsresult rv = mOmxTaskQueue->Dispatch(r.forget());
+ MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+ Unused << rv;
+ }
+}
+
+void OmxDataDecoder::EmptyBufferFailure(OmxBufferFailureHolder aFailureHolder) {
+ NotifyError(aFailureHolder.mError, __func__);
+}
+
+void OmxDataDecoder::NotifyError(OMX_ERRORTYPE aOmxError, const char* aLine,
+ const MediaResult& aError) {
+ LOG("NotifyError %d (%s) at %s", static_cast<int>(aOmxError),
+ aError.ErrorName().get(), aLine);
+ mDecodedData = DecodedData();
+ mDecodePromise.RejectIfExists(aError, __func__);
+ mDrainPromise.RejectIfExists(aError, __func__);
+ mFlushPromise.RejectIfExists(aError, __func__);
+}
+
+void OmxDataDecoder::FillAndEmptyBuffers() {
+ MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+ MOZ_ASSERT(mOmxState == OMX_StateExecuting);
+
+ // During the port setting changed, it is forbidden to do any buffer
+ // operation.
+ if (mPortSettingsChanged != -1 || mShuttingDown || mFlushing) {
+ return;
+ }
+
+ // Trigger input port.
+ while (!!mMediaRawDatas.Length()) {
+ // input buffer must be used by component if there is data available.
+ RefPtr<BufferData> inbuf = FindAvailableBuffer(OMX_DirInput);
+ if (!inbuf) {
+ LOG("no input buffer!");
+ break;
+ }
+
+ RefPtr<MediaRawData> data = mMediaRawDatas[0];
+ // Buffer size should large enough for raw data.
+ MOZ_RELEASE_ASSERT(inbuf->mBuffer->nAllocLen >= data->Size());
+
+ memcpy(inbuf->mBuffer->pBuffer, data->Data(), data->Size());
+ inbuf->mBuffer->nFilledLen = data->Size();
+ inbuf->mBuffer->nOffset = 0;
+ inbuf->mBuffer->nFlags = inbuf->mBuffer->nAllocLen > data->Size()
+ ? OMX_BUFFERFLAG_ENDOFFRAME
+ : 0;
+ inbuf->mBuffer->nTimeStamp = data->mTime.ToMicroseconds();
+ if (data->Size()) {
+ inbuf->mRawData = mMediaRawDatas[0];
+ } else {
+ LOG("send EOS buffer");
+ inbuf->mBuffer->nFlags |= OMX_BUFFERFLAG_EOS;
+ }
+
+ LOG("feed sample %p to omx component, len %ld, flag %lX", data.get(),
+ inbuf->mBuffer->nFilledLen, inbuf->mBuffer->nFlags);
+ mOmxLayer->EmptyBuffer(inbuf)->Then(mOmxTaskQueue, __func__, this,
+ &OmxDataDecoder::EmptyBufferDone,
+ &OmxDataDecoder::EmptyBufferFailure);
+ mMediaRawDatas.RemoveElementAt(0);
+ }
+
+ // Trigger output port.
+ while (true) {
+ RefPtr<BufferData> outbuf = FindAvailableBuffer(OMX_DirOutput);
+ if (!outbuf) {
+ break;
+ }
+
+ mOmxLayer->FillBuffer(outbuf)->Then(mOmxTaskQueue, __func__, this,
+ &OmxDataDecoder::FillBufferDone,
+ &OmxDataDecoder::FillBufferFailure);
+ }
+}
+
+OmxPromiseLayer::BufferData* OmxDataDecoder::FindAvailableBuffer(
+ OMX_DIRTYPE aType) {
+ BUFFERLIST* buffers = GetBuffers(aType);
+
+ for (uint32_t i = 0; i < buffers->Length(); i++) {
+ BufferData* buf = buffers->ElementAt(i);
+ if (buf->mStatus == BufferData::BufferStatus::FREE) {
+ return buf;
+ }
+ }
+
+ return nullptr;
+}
+
+nsresult OmxDataDecoder::AllocateBuffers(OMX_DIRTYPE aType) {
+ MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+ return mOmxLayer->AllocateOmxBuffer(aType, GetBuffers(aType));
+}
+
+nsresult OmxDataDecoder::ReleaseBuffers(OMX_DIRTYPE aType) {
+ MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+ return mOmxLayer->ReleaseOmxBuffer(aType, GetBuffers(aType));
+}
+
+nsTArray<RefPtr<OmxPromiseLayer::BufferData>>* OmxDataDecoder::GetBuffers(
+ OMX_DIRTYPE aType) {
+ MOZ_ASSERT(aType == OMX_DIRTYPE::OMX_DirInput ||
+ aType == OMX_DIRTYPE::OMX_DirOutput);
+
+ if (aType == OMX_DIRTYPE::OMX_DirInput) {
+ return &mInPortBuffers;
+ }
+ return &mOutPortBuffers;
+}
+
+void OmxDataDecoder::ResolveInitPromise(const char* aMethodName) {
+ MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+ LOG("called from %s", aMethodName);
+ mInitPromise.ResolveIfExists(mTrackInfo->GetType(), aMethodName);
+}
+
+void OmxDataDecoder::RejectInitPromise(MediaResult aError,
+ const char* aMethodName) {
+ MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+ mInitPromise.RejectIfExists(aError, aMethodName);
+}
+
+void OmxDataDecoder::OmxStateRunner() {
+ MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+ LOG("OMX state: %s", StateTypeToStr(mOmxState));
+
+ // TODO: maybe it'd be better to use promise CompletionPromise() to replace
+ // this state machine.
+ if (mOmxState == OMX_StateLoaded) {
+ ConfigCodec();
+
+ // Send OpenMax state command to OMX_StateIdle.
+ RefPtr<OmxDataDecoder> self = this;
+ mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateIdle, nullptr)
+ ->Then(
+ mOmxTaskQueue, __func__,
+ [self]() {
+ // Current state should be OMX_StateIdle.
+ self->mOmxState = self->mOmxLayer->GetState();
+ MOZ_ASSERT(self->mOmxState == OMX_StateIdle);
+ },
+ [self]() {
+ self->RejectInitPromise(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
+ });
+
+ // Allocate input and output buffers.
+ OMX_DIRTYPE types[] = {OMX_DIRTYPE::OMX_DirInput,
+ OMX_DIRTYPE::OMX_DirOutput};
+ for (const auto id : types) {
+ if (NS_FAILED(AllocateBuffers(id))) {
+ LOG("Failed to allocate buffer on port %d", id);
+ RejectInitPromise(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
+ break;
+ }
+ }
+ } else if (mOmxState == OMX_StateIdle) {
+ RefPtr<OmxDataDecoder> self = this;
+ mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateExecuting, nullptr)
+ ->Then(
+ mOmxTaskQueue, __func__,
+ [self]() {
+ self->mOmxState = self->mOmxLayer->GetState();
+ MOZ_ASSERT(self->mOmxState == OMX_StateExecuting);
+
+ self->ResolveInitPromise(__func__);
+ },
+ [self]() {
+ self->RejectInitPromise(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
+ });
+ } else if (mOmxState == OMX_StateExecuting) {
+ // Configure codec once it gets OMX_StateExecuting state.
+ FillCodecConfigDataToOmx();
+ } else {
+ MOZ_ASSERT(0);
+ }
+}
+
+void OmxDataDecoder::ConfigCodec() {
+ OMX_ERRORTYPE err = mOmxLayer->Config();
+ CHECK_OMX_ERR(err);
+}
+
+void OmxDataDecoder::FillCodecConfigDataToOmx() {
+ // Codec configure data should be the first sample running on Omx TaskQueue.
+ MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+ MOZ_ASSERT(!mMediaRawDatas.Length());
+ MOZ_ASSERT(mOmxState == OMX_StateIdle || mOmxState == OMX_StateExecuting);
+
+ RefPtr<BufferData> inbuf = FindAvailableBuffer(OMX_DirInput);
+ RefPtr<MediaByteBuffer> csc;
+ if (mTrackInfo->IsAudio()) {
+ // It would be nice to instead use more specific information here, but
+ // we force a byte buffer for now since this handles arbitrary codecs.
+ // TODO(bug 1768566): implement further type checking for codec data.
+ csc = ForceGetAudioCodecSpecificBlob(
+ mTrackInfo->GetAsAudioInfo()->mCodecSpecificConfig);
+ } else if (mTrackInfo->IsVideo()) {
+ csc = mTrackInfo->GetAsVideoInfo()->mExtraData;
+ }
+
+ MOZ_RELEASE_ASSERT(csc);
+
+ // Some codecs like h264, its codec specific data is at the first packet, not
+ // in container.
+ if (csc->Length()) {
+ // Buffer size should large enough for raw data.
+ MOZ_RELEASE_ASSERT(inbuf->mBuffer->nAllocLen >= csc->Length());
+
+ memcpy(inbuf->mBuffer->pBuffer, csc->Elements(), csc->Length());
+ inbuf->mBuffer->nFilledLen = csc->Length();
+ inbuf->mBuffer->nOffset = 0;
+ inbuf->mBuffer->nFlags =
+ (OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG);
+
+ LOG("Feed codec configure data to OMX component");
+ mOmxLayer->EmptyBuffer(inbuf)->Then(mOmxTaskQueue, __func__, this,
+ &OmxDataDecoder::EmptyBufferDone,
+ &OmxDataDecoder::EmptyBufferFailure);
+ }
+}
+
+bool OmxDataDecoder::Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1,
+ OMX_U32 aData2) {
+ MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+ if (mOmxLayer->Event(aEvent, aData1, aData2)) {
+ return true;
+ }
+
+ switch (aEvent) {
+ case OMX_EventPortSettingsChanged: {
+ // Don't always disable port. See bug 1235340.
+ if (aData2 == 0 || aData2 == OMX_IndexParamPortDefinition) {
+ // According to spec: "To prevent the loss of any input data, the
+ // component issuing the OMX_EventPortSettingsChanged event on its input
+ // port should buffer all input port data that arrives between the
+ // emission of the OMX_EventPortSettingsChanged event and the arrival of
+ // the command to disable the input port."
+ //
+ // So client needs to disable port and reallocate buffers.
+ MOZ_ASSERT(mPortSettingsChanged == -1);
+ mPortSettingsChanged = aData1;
+ }
+ LOG("Got OMX_EventPortSettingsChanged event");
+ break;
+ }
+ default: {
+ // Got error during decoding, send msg to MFR skipping to next key frame.
+ if (aEvent == OMX_EventError && mOmxState == OMX_StateExecuting) {
+ NotifyError((OMX_ERRORTYPE)aData1, __func__,
+ MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__));
+ return true;
+ }
+ LOG("WARNING: got none handle event: %d, aData1: %ld, aData2: %ld",
+ aEvent, aData1, aData2);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool OmxDataDecoder::BuffersCanBeReleased(OMX_DIRTYPE aType) {
+ BUFFERLIST* buffers = GetBuffers(aType);
+ uint32_t len = buffers->Length();
+ for (uint32_t i = 0; i < len; i++) {
+ BufferData::BufferStatus buf_status = buffers->ElementAt(i)->mStatus;
+ if (buf_status == BufferData::BufferStatus::OMX_COMPONENT ||
+ buf_status == BufferData::BufferStatus::OMX_CLIENT_OUTPUT) {
+ return false;
+ }
+ }
+ return true;
+}
+
+OMX_DIRTYPE
+OmxDataDecoder::GetPortDirection(uint32_t aPortIndex) {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOmxParameter(&def);
+ def.nPortIndex = mPortSettingsChanged;
+
+ OMX_ERRORTYPE err =
+ mOmxLayer->GetParameter(OMX_IndexParamPortDefinition, &def, sizeof(def));
+ if (err != OMX_ErrorNone) {
+ return OMX_DirMax;
+ }
+ return def.eDir;
+}
+
+RefPtr<OmxPromiseLayer::OmxBufferPromise::AllPromiseType>
+OmxDataDecoder::CollectBufferPromises(OMX_DIRTYPE aType) {
+ MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+ nsTArray<RefPtr<OmxBufferPromise>> promises;
+ OMX_DIRTYPE types[] = {OMX_DIRTYPE::OMX_DirInput, OMX_DIRTYPE::OMX_DirOutput};
+ for (const auto type : types) {
+ if ((aType == type) || (aType == OMX_DirMax)) {
+ // find the buffer which has promise.
+ BUFFERLIST* buffers = GetBuffers(type);
+
+ for (uint32_t i = 0; i < buffers->Length(); i++) {
+ BufferData* buf = buffers->ElementAt(i);
+ if (!buf->mPromise.IsEmpty()) {
+ // OmxBufferPromise is not exclusive, it can be multiple "Then"s, so
+ // it is safe to call "Ensure" here.
+ promises.AppendElement(buf->mPromise.Ensure(__func__));
+ }
+ }
+ }
+ }
+
+ LOG("CollectBufferPromises: type %d, total %zu promiese", aType,
+ promises.Length());
+ if (promises.Length()) {
+ return OmxBufferPromise::All(mOmxTaskQueue, promises);
+ }
+
+ return OmxBufferPromise::AllPromiseType::CreateAndResolve(
+ nsTArray<BufferData*>(), __func__);
+}
+
+void OmxDataDecoder::PortSettingsChanged() {
+ MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+ if (mPortSettingsChanged == -1 ||
+ mOmxState == OMX_STATETYPE::OMX_StateInvalid) {
+ return;
+ }
+
+ // The PortSettingsChanged algorithm:
+ //
+ // 1. disable port.
+ // 2. wait for port buffers return to client and then release these buffers.
+ // 3. enable port.
+ // 4. allocate port buffers.
+ //
+
+ // Disable port. Get port definition if the target port is enable.
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOmxParameter(&def);
+ def.nPortIndex = mPortSettingsChanged;
+
+ OMX_ERRORTYPE err =
+ mOmxLayer->GetParameter(OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_OMX_ERR(err);
+
+ RefPtr<OmxDataDecoder> self = this;
+ if (def.bEnabled) {
+ // 1. disable port.
+ LOG("PortSettingsChanged: disable port %lu", def.nPortIndex);
+ mOmxLayer
+ ->SendCommand(OMX_CommandPortDisable, mPortSettingsChanged, nullptr)
+ ->Then(
+ mOmxTaskQueue, __func__,
+ [self, def]() -> RefPtr<OmxCommandPromise> {
+ // 3. enable port.
+ // Send enable port command.
+ RefPtr<OmxCommandPromise> p = self->mOmxLayer->SendCommand(
+ OMX_CommandPortEnable, self->mPortSettingsChanged, nullptr);
+
+ // 4. allocate port buffers.
+ // Allocate new port buffers.
+ nsresult rv = self->AllocateBuffers(def.eDir);
+ if (NS_FAILED(rv)) {
+ self->NotifyError(OMX_ErrorUndefined, __func__);
+ }
+
+ return p;
+ },
+ [self](const OmxCommandFailureHolder& aError) {
+ self->NotifyError(OMX_ErrorUndefined, __func__);
+ return OmxCommandPromise::CreateAndReject(aError, __func__);
+ })
+ ->Then(
+ mOmxTaskQueue, __func__,
+ [self]() {
+ LOGL("PortSettingsChanged: port settings changed complete");
+ // finish port setting changed.
+ self->mPortSettingsChanged = -1;
+ self->FillAndEmptyBuffers();
+ },
+ [self]() { self->NotifyError(OMX_ErrorUndefined, __func__); });
+
+ // 2. wait for port buffers return to client and then release these buffers.
+ //
+ // Port buffers will be returned to client soon once OMX_CommandPortDisable
+ // command is sent. Then releasing these buffers.
+ CollectBufferPromises(def.eDir)->Then(
+ mOmxTaskQueue, __func__,
+ [self, def]() {
+ MOZ_ASSERT(self->BuffersCanBeReleased(def.eDir));
+ nsresult rv = self->ReleaseBuffers(def.eDir);
+ if (NS_FAILED(rv)) {
+ MOZ_RELEASE_ASSERT(0);
+ self->NotifyError(OMX_ErrorUndefined, __func__);
+ }
+ },
+ [self]() { self->NotifyError(OMX_ErrorUndefined, __func__); });
+ }
+}
+
+void OmxDataDecoder::SendEosBuffer() {
+ MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+ // There is no 'Drain' API in OpenMax, so it needs to wait for output sample
+ // with EOS flag. However, MediaRawData doesn't provide EOS information,
+ // so here it generates an empty BufferData with eos OMX_BUFFERFLAG_EOS in
+ // queue. This behaviour should be compliant with spec, I think...
+ RefPtr<MediaRawData> eos_data = new MediaRawData();
+ mMediaRawDatas.AppendElement(eos_data);
+ FillAndEmptyBuffers();
+}
+
+RefPtr<MediaDataDecoder::FlushPromise> OmxDataDecoder::DoFlush() {
+ MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+ mDecodedData = DecodedData();
+ mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+ mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+ mPerformanceRecorder.Record(std::numeric_limits<int64_t>::max());
+
+ RefPtr<FlushPromise> p = mFlushPromise.Ensure(__func__);
+
+ // 1. Call OMX command OMX_CommandFlush in Omx TaskQueue.
+ // 2. Remove all elements in mMediaRawDatas when flush is completed.
+ mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
+ ->Then(mOmxTaskQueue, __func__, this, &OmxDataDecoder::FlushComplete,
+ &OmxDataDecoder::FlushFailure);
+
+ return p;
+}
+
+void OmxDataDecoder::FlushComplete(OMX_COMMANDTYPE aCommandType) {
+ mMediaRawDatas.Clear();
+ mFlushing = false;
+
+ LOG("Flush complete");
+ mFlushPromise.ResolveIfExists(true, __func__);
+}
+
+void OmxDataDecoder::FlushFailure(OmxCommandFailureHolder aFailureHolder) {
+ mFlushing = false;
+ mFlushPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
+}
+
+MediaDataHelper::MediaDataHelper(const TrackInfo* aTrackInfo,
+ layers::ImageContainer* aImageContainer,
+ OmxPromiseLayer* aOmxLayer)
+ : mTrackInfo(aTrackInfo),
+ mAudioCompactor(mAudioQueue),
+ mImageContainer(aImageContainer) {
+ InitOmxParameter(&mOutputPortDef);
+ mOutputPortDef.nPortIndex = aOmxLayer->OutputPortIndex();
+ aOmxLayer->GetParameter(OMX_IndexParamPortDefinition, &mOutputPortDef,
+ sizeof(mOutputPortDef));
+}
+
+already_AddRefed<MediaData> MediaDataHelper::GetMediaData(
+ BufferData* aBufferData, bool& aPlatformDepenentData) {
+ aPlatformDepenentData = false;
+ RefPtr<MediaData> data;
+
+ if (mTrackInfo->IsAudio()) {
+ if (!aBufferData->mBuffer->nFilledLen) {
+ return nullptr;
+ }
+ data = CreateAudioData(aBufferData);
+ } else if (mTrackInfo->IsVideo()) {
+ data = aBufferData->GetPlatformMediaData();
+ if (data) {
+ aPlatformDepenentData = true;
+ } else {
+ if (!aBufferData->mBuffer->nFilledLen) {
+ return nullptr;
+ }
+ // Get YUV VideoData, it uses more CPU, in most cases, on software codec.
+ data = CreateYUV420VideoData(aBufferData);
+ }
+
+ // Update video time code, duration... from the raw data.
+ VideoData* video(data->As<VideoData>());
+ if (aBufferData->mRawData) {
+ video->mTime = aBufferData->mRawData->mTime;
+ video->mTimecode = aBufferData->mRawData->mTimecode;
+ video->mOffset = aBufferData->mRawData->mOffset;
+ video->mDuration = aBufferData->mRawData->mDuration;
+ video->mKeyframe = aBufferData->mRawData->mKeyframe;
+ }
+ }
+
+ return data.forget();
+}
+
+already_AddRefed<AudioData> MediaDataHelper::CreateAudioData(
+ BufferData* aBufferData) {
+ RefPtr<AudioData> audio;
+ OMX_BUFFERHEADERTYPE* buf = aBufferData->mBuffer;
+ const AudioInfo* info = mTrackInfo->GetAsAudioInfo();
+ if (buf->nFilledLen) {
+ uint64_t offset = 0;
+ uint32_t frames = buf->nFilledLen / (2 * info->mChannels);
+ if (aBufferData->mRawData) {
+ offset = aBufferData->mRawData->mOffset;
+ }
+ typedef AudioCompactor::NativeCopy OmxCopy;
+ mAudioCompactor.Push(
+ offset, buf->nTimeStamp, info->mRate, frames, info->mChannels,
+ OmxCopy(buf->pBuffer + buf->nOffset, buf->nFilledLen, info->mChannels));
+ audio = mAudioQueue.PopFront();
+ }
+
+ return audio.forget();
+}
+
+already_AddRefed<VideoData> MediaDataHelper::CreateYUV420VideoData(
+ BufferData* aBufferData) {
+ uint8_t* yuv420p_buffer = (uint8_t*)aBufferData->mBuffer->pBuffer;
+ int32_t stride = mOutputPortDef.format.video.nStride;
+ int32_t slice_height = mOutputPortDef.format.video.nSliceHeight;
+ int32_t width = mTrackInfo->GetAsVideoInfo()->mImage.width;
+ int32_t height = mTrackInfo->GetAsVideoInfo()->mImage.height;
+
+ // TODO: convert other formats to YUV420.
+ if (mOutputPortDef.format.video.eColorFormat !=
+ OMX_COLOR_FormatYUV420Planar) {
+ return nullptr;
+ }
+
+ size_t yuv420p_y_size = stride * slice_height;
+ size_t yuv420p_u_size = ((stride + 1) / 2) * ((slice_height + 1) / 2);
+ uint8_t* yuv420p_y = yuv420p_buffer;
+ uint8_t* yuv420p_u = yuv420p_y + yuv420p_y_size;
+ uint8_t* yuv420p_v = yuv420p_u + yuv420p_u_size;
+
+ VideoData::YCbCrBuffer b;
+ b.mPlanes[0].mData = yuv420p_y;
+ b.mPlanes[0].mWidth = width;
+ b.mPlanes[0].mHeight = height;
+ b.mPlanes[0].mStride = stride;
+ b.mPlanes[0].mSkip = 0;
+
+ b.mPlanes[1].mData = yuv420p_u;
+ b.mPlanes[1].mWidth = (width + 1) / 2;
+ b.mPlanes[1].mHeight = (height + 1) / 2;
+ b.mPlanes[1].mStride = (stride + 1) / 2;
+ b.mPlanes[1].mSkip = 0;
+
+ b.mPlanes[2].mData = yuv420p_v;
+ b.mPlanes[2].mWidth = (width + 1) / 2;
+ b.mPlanes[2].mHeight = (height + 1) / 2;
+ b.mPlanes[2].mStride = (stride + 1) / 2;
+ b.mPlanes[2].mSkip = 0;
+
+ b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
+
+ VideoInfo info(*mTrackInfo->GetAsVideoInfo());
+
+ auto maybeColorSpace = info.mColorSpace;
+ if (!maybeColorSpace) {
+ maybeColorSpace = Some(DefaultColorSpace({width, height}));
+ }
+ b.mYUVColorSpace = *maybeColorSpace;
+
+ auto maybeColorPrimaries = info.mColorPrimaries;
+ if (!maybeColorPrimaries) {
+ maybeColorPrimaries = Some(gfx::ColorSpace2::BT709);
+ }
+ b.mColorPrimaries = *maybeColorPrimaries;
+
+ Result<already_AddRefed<VideoData>, MediaResult> result =
+ VideoData::CreateAndCopyData(
+ info, mImageContainer,
+ 0, // Filled later by caller.
+ media::TimeUnit::Zero(), // Filled later by caller.
+ media::TimeUnit::FromMicroseconds(1), // We don't know the duration.
+ b,
+ false, // Filled later by caller.
+ media::TimeUnit::FromMicroseconds(-1), info.ImageRect(), nullptr);
+
+ if (result.isErr()) {
+ MediaResult r = result.unwrapErr();
+ MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug,
+ ("Failed to create a YUV420 VideoData - %s: %s",
+ r.ErrorName().get(), r.Message().get()));
+ return nullptr;
+ }
+
+ RefPtr<VideoData> data = result.unwrap();
+ MOZ_ASSERT(data);
+ MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug,
+ ("YUV420 VideoData: disp width %d, height %d, pic width %d, height "
+ "%d, time %lld",
+ info.mDisplay.width, info.mDisplay.height, info.mImage.width,
+ info.mImage.height, aBufferData->mBuffer->nTimeStamp));
+
+ return data.forget();
+}
+
+} // namespace mozilla
diff --git a/dom/media/platforms/omx/OmxDataDecoder.h b/dom/media/platforms/omx/OmxDataDecoder.h
new file mode 100644
index 0000000000..69c388ecee
--- /dev/null
+++ b/dom/media/platforms/omx/OmxDataDecoder.h
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(OmxDataDecoder_h_)
+# define OmxDataDecoder_h_
+
+# include "AudioCompactor.h"
+# include "ImageContainer.h"
+# include "MediaInfo.h"
+# include "OMX_Component.h"
+# include "OmxPromiseLayer.h"
+# include "PerformanceRecorder.h"
+# include "PlatformDecoderModule.h"
+# include "mozilla/Monitor.h"
+# include "mozilla/StateWatching.h"
+
+namespace mozilla {
+
+class MediaDataHelper;
+
+typedef OmxPromiseLayer::OmxCommandPromise OmxCommandPromise;
+typedef OmxPromiseLayer::OmxBufferPromise OmxBufferPromise;
+typedef OmxPromiseLayer::OmxBufferFailureHolder OmxBufferFailureHolder;
+typedef OmxPromiseLayer::OmxCommandFailureHolder OmxCommandFailureHolder;
+typedef OmxPromiseLayer::BufferData BufferData;
+typedef OmxPromiseLayer::BUFFERLIST BUFFERLIST;
+
+DDLoggedTypeDeclNameAndBase(OmxDataDecoder, MediaDataDecoder);
+
+/* OmxDataDecoder is the major class which performs followings:
+ * 1. Translate PDM function into OMX commands.
+ * 2. Keeping the buffers between client and component.
+ * 3. Manage the OMX state.
+ *
+ * From the definition in OpenMax spec. "2.2.1", there are 3 major roles in
+ * OpenMax IL.
+ *
+ * IL client:
+ * "The IL client may be a layer below the GUI application, such as GStreamer,
+ * or may be several layers below the GUI layer."
+ *
+ * OmxDataDecoder acts as the IL client.
+ *
+ * OpenMAX IL component:
+ * "A component that is intended to wrap functionality that is required in the
+ * target system."
+ *
+ * OmxPromiseLayer acts as the OpenMAX IL component.
+ *
+ * OpenMAX IL core:
+ * "Platform-specific code that has the functionality necessary to locate and
+ * then load an OpenMAX IL component into main memory."
+ *
+ * OmxPlatformLayer acts as the OpenMAX IL core.
+ */
+class OmxDataDecoder final : public MediaDataDecoder,
+ public DecoderDoctorLifeLogger<OmxDataDecoder> {
+ protected:
+ virtual ~OmxDataDecoder();
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OmxDataDecoder, final);
+
+ OmxDataDecoder(const TrackInfo& aTrackInfo,
+ layers::ImageContainer* aImageContainer,
+ Maybe<TrackingId> aTrackingId);
+
+ RefPtr<InitPromise> Init() override;
+ RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
+ RefPtr<DecodePromise> Drain() override;
+ RefPtr<FlushPromise> Flush() override;
+ RefPtr<ShutdownPromise> Shutdown() override;
+
+ nsCString GetDescriptionName() const override { return "omx decoder"_ns; }
+
+ nsCString GetCodecName() const override { return "unknown"_ns; }
+
+ ConversionRequired NeedsConversion() const override {
+ return ConversionRequired::kNeedAnnexB;
+ }
+
+ // Return true if event is handled.
+ bool Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2);
+
+ protected:
+ void InitializationTask();
+
+ void ResolveInitPromise(const char* aMethodName);
+
+ void RejectInitPromise(MediaResult aError, const char* aMethodName);
+
+ void OmxStateRunner();
+
+ void FillAndEmptyBuffers();
+
+ void FillBufferDone(BufferData* aData);
+
+ void FillBufferFailure(OmxBufferFailureHolder aFailureHolder);
+
+ void EmptyBufferDone(BufferData* aData);
+
+ void EmptyBufferFailure(OmxBufferFailureHolder aFailureHolder);
+
+ void NotifyError(
+ OMX_ERRORTYPE aOmxError, const char* aLine,
+ const MediaResult& aError = MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR));
+
+ // Configure audio/video codec.
+ // Some codec may just ignore this and rely on codec specific data in
+ // FillCodecConfigDataToOmx().
+ void ConfigCodec();
+
+ // Sending codec specific data to OMX component. OMX component could send a
+ // OMX_EventPortSettingsChanged back to client. And then client needs to
+ // disable port and reallocate buffer.
+ void FillCodecConfigDataToOmx();
+
+ void SendEosBuffer();
+
+ void EndOfStream();
+
+ // It could be called after codec specific data is sent and component found
+ // the port format is changed due to different codec specific.
+ void PortSettingsChanged();
+
+ void Output(BufferData* aData);
+
+ // Buffer can be released if its status is not OMX_COMPONENT or
+ // OMX_CLIENT_OUTPUT.
+ bool BuffersCanBeReleased(OMX_DIRTYPE aType);
+
+ OMX_DIRTYPE GetPortDirection(uint32_t aPortIndex);
+
+ RefPtr<ShutdownPromise> DoAsyncShutdown();
+
+ RefPtr<FlushPromise> DoFlush();
+
+ void FlushComplete(OMX_COMMANDTYPE aCommandType);
+
+ void FlushFailure(OmxCommandFailureHolder aFailureHolder);
+
+ BUFFERLIST* GetBuffers(OMX_DIRTYPE aType);
+
+ nsresult AllocateBuffers(OMX_DIRTYPE aType);
+
+ nsresult ReleaseBuffers(OMX_DIRTYPE aType);
+
+ BufferData* FindAvailableBuffer(OMX_DIRTYPE aType);
+
+ // aType could be OMX_DirMax for all types.
+ RefPtr<OmxPromiseLayer::OmxBufferPromise::AllPromiseType>
+ CollectBufferPromises(OMX_DIRTYPE aType);
+
+ // The Omx TaskQueue.
+ RefPtr<TaskQueue> mOmxTaskQueue;
+
+ nsCOMPtr<nsISerialEventTarget> mThread;
+ RefPtr<layers::ImageContainer> mImageContainer;
+
+ WatchManager<OmxDataDecoder> mWatchManager;
+
+ // It is accessed in omx TaskQueue.
+ Watchable<OMX_STATETYPE> mOmxState;
+
+ RefPtr<OmxPromiseLayer> mOmxLayer;
+
+ UniquePtr<TrackInfo> mTrackInfo;
+
+ // It is accessed in both omx and reader TaskQueue.
+ Atomic<bool> mFlushing;
+
+ // It is accessed in Omx/reader TaskQueue.
+ Atomic<bool> mShuttingDown;
+
+ // It is accessed in Omx TaskQeueu.
+ bool mCheckingInputExhausted;
+
+ // It is accessed in OMX TaskQueue.
+ MozPromiseHolder<InitPromise> mInitPromise;
+ MozPromiseHolder<DecodePromise> mDecodePromise;
+ MozPromiseHolder<DecodePromise> mDrainPromise;
+ MozPromiseHolder<FlushPromise> mFlushPromise;
+ MozPromiseHolder<ShutdownPromise> mShutdownPromise;
+ // Where decoded samples will be stored until the decode promise is resolved.
+ DecodedData mDecodedData;
+
+ void CompleteDrain();
+
+ // It is written in Omx TaskQueue. Read in Omx TaskQueue.
+ // It value means the port index which port settings is changed.
+ // -1 means no port setting changed.
+ //
+ // Note: when port setting changed, there should be no buffer operations
+ // via EmptyBuffer or FillBuffer.
+ Watchable<int32_t> mPortSettingsChanged;
+
+ // It is access in Omx TaskQueue.
+ nsTArray<RefPtr<MediaRawData>> mMediaRawDatas;
+
+ BUFFERLIST mInPortBuffers;
+
+ BUFFERLIST mOutPortBuffers;
+
+ RefPtr<MediaDataHelper> mMediaDataHelper;
+
+ const Maybe<TrackingId> mTrackingId;
+
+ // Accessed on Omx TaskQueue
+ PerformanceRecorderMulti<DecodeStage> mPerformanceRecorder;
+};
+
+template <class T>
+void InitOmxParameter(T* aParam) {
+ PodZero(aParam);
+ aParam->nSize = sizeof(T);
+ aParam->nVersion.s.nVersionMajor = 1;
+}
+
+} // namespace mozilla
+
+#endif /* OmxDataDecoder_h_ */
diff --git a/dom/media/platforms/omx/OmxDecoderModule.cpp b/dom/media/platforms/omx/OmxDecoderModule.cpp
new file mode 100644
index 0000000000..c6945cd5a1
--- /dev/null
+++ b/dom/media/platforms/omx/OmxDecoderModule.cpp
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OmxDecoderModule.h"
+
+#include "OmxDataDecoder.h"
+#include "OmxPlatformLayer.h"
+
+#ifdef MOZ_OMX
+# include "PureOmxPlatformLayer.h"
+#endif
+
+namespace mozilla {
+
+/* static */
+bool OmxDecoderModule::Init() {
+#ifdef MOZ_OMX
+ return PureOmxPlatformLayer::Init();
+#endif
+ return false;
+}
+
+OmxDecoderModule* OmxDecoderModule::Create() {
+#ifdef MOZ_OMX
+ if (Init()) {
+ return new OmxDecoderModule();
+ }
+#endif
+ return nullptr;
+}
+
+already_AddRefed<MediaDataDecoder> OmxDecoderModule::CreateVideoDecoder(
+ const CreateDecoderParams& aParams) {
+ RefPtr<OmxDataDecoder> decoder = new OmxDataDecoder(
+ aParams.mConfig, aParams.mImageContainer, aParams.mTrackingId);
+ return decoder.forget();
+}
+
+already_AddRefed<MediaDataDecoder> OmxDecoderModule::CreateAudioDecoder(
+ const CreateDecoderParams& aParams) {
+ RefPtr<OmxDataDecoder> decoder =
+ new OmxDataDecoder(aParams.mConfig, nullptr, aParams.mTrackingId);
+ return decoder.forget();
+}
+
+media::DecodeSupportSet OmxDecoderModule::SupportsMimeType(
+ const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const {
+ if (OmxPlatformLayer::SupportsMimeType(aMimeType)) {
+ // TODO: Note that we do not yet distinguish between SW/HW decode support.
+ // Will be done in bug 1754239.
+ return media::DecodeSupport::SoftwareDecode;
+ }
+ return media::DecodeSupportSet{};
+}
+
+} // namespace mozilla
diff --git a/dom/media/platforms/omx/OmxDecoderModule.h b/dom/media/platforms/omx/OmxDecoderModule.h
new file mode 100644
index 0000000000..04fa809e54
--- /dev/null
+++ b/dom/media/platforms/omx/OmxDecoderModule.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(OmxDecoderModule_h_)
+# define OmxDecoderModule_h_
+
+# include "PlatformDecoderModule.h"
+
+namespace mozilla {
+
+class OmxDecoderModule : public PlatformDecoderModule {
+ public:
+ // Called on main thread.
+ static bool Init();
+ static OmxDecoderModule* Create();
+
+ already_AddRefed<MediaDataDecoder> CreateVideoDecoder(
+ const CreateDecoderParams& aParams) override;
+
+ already_AddRefed<MediaDataDecoder> CreateAudioDecoder(
+ const CreateDecoderParams& aParams) override;
+
+ media::DecodeSupportSet SupportsMimeType(
+ const nsACString& aMimeType,
+ DecoderDoctorDiagnostics* aDiagnostics) const override;
+};
+
+} // namespace mozilla
+
+#endif // OmxDecoderModule_h_
diff --git a/dom/media/platforms/omx/OmxFunctionList.h b/dom/media/platforms/omx/OmxFunctionList.h
new file mode 100644
index 0000000000..e1e92bfe65
--- /dev/null
+++ b/dom/media/platforms/omx/OmxFunctionList.h
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+OMX_FUNC(OMX_Init)
+OMX_FUNC(OMX_Deinit)
+OMX_FUNC(OMX_GetHandle)
+OMX_FUNC(OMX_FreeHandle)
+OMX_FUNC(OMX_ComponentNameEnum)
+OMX_FUNC(OMX_GetComponentsOfRole)
+OMX_FUNC(OMX_GetRolesOfComponent)
+OMX_FUNC(OMX_SetupTunnel)
+OMX_FUNC(OMX_GetContentPipe)
diff --git a/dom/media/platforms/omx/OmxPlatformLayer.cpp b/dom/media/platforms/omx/OmxPlatformLayer.cpp
new file mode 100644
index 0000000000..dc3ccc0979
--- /dev/null
+++ b/dom/media/platforms/omx/OmxPlatformLayer.cpp
@@ -0,0 +1,307 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OmxPlatformLayer.h"
+
+#include "OmxDataDecoder.h"
+#include "OMX_Component.h"
+#include "OMX_VideoExt.h" // For VP8.
+
+#ifdef MOZ_OMX
+# include "PureOmxPlatformLayer.h"
+#endif
+
+#include "VPXDecoder.h"
+
+#ifdef LOG
+# undef LOG
+#endif
+
+#define LOG(arg, ...) \
+ MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, \
+ ("OmxPlatformLayer -- %s: " arg, __func__, ##__VA_ARGS__))
+
+#define RETURN_IF_ERR(err) \
+ if (err != OMX_ErrorNone) { \
+ LOG("error: 0x%08x", err); \
+ return err; \
+ }
+
+// Common OMX decoder configuration code.
+namespace mozilla {
+
+// This helper class encapsulates the details of component parameters setting
+// for different OMX audio & video codecs.
+template <typename ParamType>
+class OmxConfig {
+ public:
+ virtual ~OmxConfig() = default;
+ // Subclasses should implement this method to configure the codec.
+ virtual OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx,
+ const ParamType& aParam) = 0;
+};
+
+typedef OmxConfig<AudioInfo> OmxAudioConfig;
+typedef OmxConfig<VideoInfo> OmxVideoConfig;
+
+template <typename ConfigType>
+UniquePtr<ConfigType> ConfigForMime(const nsACString&);
+
+static OMX_ERRORTYPE ConfigAudioOutputPort(OmxPlatformLayer& aOmx,
+ const AudioInfo& aInfo) {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOmxParameter(&def);
+ def.nPortIndex = aOmx.OutputPortIndex();
+ OMX_ERRORTYPE err =
+ aOmx.GetParameter(OMX_IndexParamPortDefinition, &def, sizeof(def));
+ RETURN_IF_ERR(err);
+
+ def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
+ err = aOmx.SetParameter(OMX_IndexParamPortDefinition, &def, sizeof(def));
+ RETURN_IF_ERR(err);
+
+ OMX_AUDIO_PARAM_PCMMODETYPE pcmParams;
+ InitOmxParameter(&pcmParams);
+ pcmParams.nPortIndex = def.nPortIndex;
+ err =
+ aOmx.GetParameter(OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams));
+ RETURN_IF_ERR(err);
+
+ pcmParams.nChannels = aInfo.mChannels;
+ pcmParams.eNumData = OMX_NumericalDataSigned;
+ pcmParams.bInterleaved = OMX_TRUE;
+ pcmParams.nBitPerSample = 16;
+ pcmParams.nSamplingRate = aInfo.mRate;
+ pcmParams.ePCMMode = OMX_AUDIO_PCMModeLinear;
+ err =
+ aOmx.SetParameter(OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams));
+ RETURN_IF_ERR(err);
+
+ LOG("Config OMX_IndexParamAudioPcm, channel %lu, sample rate %lu",
+ pcmParams.nChannels, pcmParams.nSamplingRate);
+
+ return OMX_ErrorNone;
+}
+
+class OmxAacConfig : public OmxAudioConfig {
+ public:
+ OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const AudioInfo& aInfo) override {
+ OMX_AUDIO_PARAM_AACPROFILETYPE aacProfile;
+ InitOmxParameter(&aacProfile);
+ aacProfile.nPortIndex = aOmx.InputPortIndex();
+ OMX_ERRORTYPE err = aOmx.GetParameter(OMX_IndexParamAudioAac, &aacProfile,
+ sizeof(aacProfile));
+ RETURN_IF_ERR(err);
+
+ aacProfile.nChannels = aInfo.mChannels;
+ aacProfile.nSampleRate = aInfo.mRate;
+ aacProfile.eAACProfile =
+ static_cast<OMX_AUDIO_AACPROFILETYPE>(aInfo.mProfile);
+ err = aOmx.SetParameter(OMX_IndexParamAudioAac, &aacProfile,
+ sizeof(aacProfile));
+ RETURN_IF_ERR(err);
+
+ LOG("Config OMX_IndexParamAudioAac, channel %lu, sample rate %lu, profile "
+ "%d",
+ aacProfile.nChannels, aacProfile.nSampleRate, aacProfile.eAACProfile);
+
+ return ConfigAudioOutputPort(aOmx, aInfo);
+ }
+};
+
+class OmxMp3Config : public OmxAudioConfig {
+ public:
+ OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const AudioInfo& aInfo) override {
+ OMX_AUDIO_PARAM_MP3TYPE mp3Param;
+ InitOmxParameter(&mp3Param);
+ mp3Param.nPortIndex = aOmx.InputPortIndex();
+ OMX_ERRORTYPE err =
+ aOmx.GetParameter(OMX_IndexParamAudioMp3, &mp3Param, sizeof(mp3Param));
+ RETURN_IF_ERR(err);
+
+ mp3Param.nChannels = aInfo.mChannels;
+ mp3Param.nSampleRate = aInfo.mRate;
+ err =
+ aOmx.SetParameter(OMX_IndexParamAudioMp3, &mp3Param, sizeof(mp3Param));
+ RETURN_IF_ERR(err);
+
+ LOG("Config OMX_IndexParamAudioMp3, channel %lu, sample rate %lu",
+ mp3Param.nChannels, mp3Param.nSampleRate);
+
+ return ConfigAudioOutputPort(aOmx, aInfo);
+ }
+};
+
+enum OmxAmrSampleRate {
+ kNarrowBand = 8000,
+ kWideBand = 16000,
+};
+
+template <OmxAmrSampleRate R>
+class OmxAmrConfig : public OmxAudioConfig {
+ public:
+ OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const AudioInfo& aInfo) override {
+ OMX_AUDIO_PARAM_AMRTYPE def;
+ InitOmxParameter(&def);
+ def.nPortIndex = aOmx.InputPortIndex();
+ OMX_ERRORTYPE err =
+ aOmx.GetParameter(OMX_IndexParamAudioAmr, &def, sizeof(def));
+ RETURN_IF_ERR(err);
+
+ def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
+ err = aOmx.SetParameter(OMX_IndexParamAudioAmr, &def, sizeof(def));
+ RETURN_IF_ERR(err);
+
+ MOZ_ASSERT(aInfo.mChannels == 1);
+ MOZ_ASSERT(aInfo.mRate == R);
+
+ return ConfigAudioOutputPort(aOmx, aInfo);
+ }
+};
+
+template <>
+UniquePtr<OmxAudioConfig> ConfigForMime(const nsACString& aMimeType) {
+ UniquePtr<OmxAudioConfig> conf;
+
+ if (OmxPlatformLayer::SupportsMimeType(aMimeType)) {
+ if (aMimeType.EqualsLiteral("audio/mp4a-latm")) {
+ conf.reset(new OmxAacConfig());
+ } else if (aMimeType.EqualsLiteral("audio/mp3") ||
+ aMimeType.EqualsLiteral("audio/mpeg")) {
+ conf.reset(new OmxMp3Config());
+ } else if (aMimeType.EqualsLiteral("audio/3gpp")) {
+ conf.reset(new OmxAmrConfig<OmxAmrSampleRate::kNarrowBand>());
+ } else if (aMimeType.EqualsLiteral("audio/amr-wb")) {
+ conf.reset(new OmxAmrConfig<OmxAmrSampleRate::kWideBand>());
+ }
+ }
+ return conf;
+}
+
+// There should be a better way to calculate it.
+#define MIN_VIDEO_INPUT_BUFFER_SIZE 64 * 1024
+
+class OmxCommonVideoConfig : public OmxVideoConfig {
+ public:
+ explicit OmxCommonVideoConfig() : OmxVideoConfig() {}
+
+ OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const VideoInfo& aInfo) override {
+ OMX_ERRORTYPE err = OMX_ErrorNone;
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+
+ // Set up in/out port definition.
+ nsTArray<uint32_t> ports;
+ aOmx.GetPortIndices(ports);
+ for (auto idx : ports) {
+ InitOmxParameter(&def);
+ def.nPortIndex = idx;
+ err = aOmx.GetParameter(OMX_IndexParamPortDefinition, &def, sizeof(def));
+ RETURN_IF_ERR(err);
+
+ def.format.video.nFrameWidth = aInfo.mDisplay.width;
+ def.format.video.nFrameHeight = aInfo.mDisplay.height;
+ def.format.video.nStride = aInfo.mImage.width;
+ def.format.video.nSliceHeight = aInfo.mImage.height;
+
+ if (def.eDir == OMX_DirInput) {
+ def.format.video.eCompressionFormat = aOmx.CompressionFormat();
+ def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
+ if (def.nBufferSize < MIN_VIDEO_INPUT_BUFFER_SIZE) {
+ def.nBufferSize = aInfo.mImage.width * aInfo.mImage.height;
+ LOG("Change input buffer size to %lu", def.nBufferSize);
+ }
+ } else {
+ def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+ }
+
+ err = aOmx.SetParameter(OMX_IndexParamPortDefinition, &def, sizeof(def));
+ }
+ return err;
+ }
+};
+
+template <>
+UniquePtr<OmxVideoConfig> ConfigForMime(const nsACString& aMimeType) {
+ UniquePtr<OmxVideoConfig> conf;
+
+ if (OmxPlatformLayer::SupportsMimeType(aMimeType)) {
+ conf.reset(new OmxCommonVideoConfig());
+ }
+ return conf;
+}
+
+OMX_ERRORTYPE
+OmxPlatformLayer::Config() {
+ MOZ_ASSERT(mInfo);
+
+ OMX_PORT_PARAM_TYPE portParam;
+ InitOmxParameter(&portParam);
+ if (mInfo->IsAudio()) {
+ GetParameter(OMX_IndexParamAudioInit, &portParam, sizeof(portParam));
+ mStartPortNumber = portParam.nStartPortNumber;
+ UniquePtr<OmxAudioConfig> conf(
+ ConfigForMime<OmxAudioConfig>(mInfo->mMimeType));
+ MOZ_RELEASE_ASSERT(conf.get());
+ return conf->Apply(*this, *(mInfo->GetAsAudioInfo()));
+ } else if (mInfo->IsVideo()) {
+ GetParameter(OMX_IndexParamVideoInit, &portParam, sizeof(portParam));
+ UniquePtr<OmxVideoConfig> conf(
+ ConfigForMime<OmxVideoConfig>(mInfo->mMimeType));
+ MOZ_RELEASE_ASSERT(conf.get());
+ return conf->Apply(*this, *(mInfo->GetAsVideoInfo()));
+ } else {
+ MOZ_ASSERT_UNREACHABLE("non-AV data (text?) is not supported.");
+ return OMX_ErrorNotImplemented;
+ }
+}
+
+OMX_VIDEO_CODINGTYPE
+OmxPlatformLayer::CompressionFormat() {
+ MOZ_ASSERT(mInfo);
+
+ if (mInfo->mMimeType.EqualsLiteral("video/avc")) {
+ return OMX_VIDEO_CodingAVC;
+ } else if (mInfo->mMimeType.EqualsLiteral("video/mp4v-es") ||
+ mInfo->mMimeType.EqualsLiteral("video/mp4")) {
+ return OMX_VIDEO_CodingMPEG4;
+ } else if (mInfo->mMimeType.EqualsLiteral("video/3gpp")) {
+ return OMX_VIDEO_CodingH263;
+ } else if (VPXDecoder::IsVP8(mInfo->mMimeType)) {
+ return static_cast<OMX_VIDEO_CODINGTYPE>(OMX_VIDEO_CodingVP8);
+ } else {
+ MOZ_ASSERT_UNREACHABLE("Unsupported compression format");
+ return OMX_VIDEO_CodingUnused;
+ }
+}
+
+// Implementations for different platforms will be defined in their own files.
+#if defined(MOZ_OMX)
+
+bool OmxPlatformLayer::SupportsMimeType(const nsACString& aMimeType) {
+ return PureOmxPlatformLayer::SupportsMimeType(aMimeType);
+}
+
+OmxPlatformLayer* OmxPlatformLayer::Create(
+ OmxDataDecoder* aDataDecoder, OmxPromiseLayer* aPromiseLayer,
+ TaskQueue* aTaskQueue, layers::ImageContainer* aImageContainer) {
+ return new PureOmxPlatformLayer(aDataDecoder, aPromiseLayer, aTaskQueue,
+ aImageContainer);
+}
+
+#else // For platforms without OMX IL support.
+
+bool OmxPlatformLayer::SupportsMimeType(const nsACString& aMimeType) {
+ return false;
+}
+
+OmxPlatformLayer* OmxPlatformLayer::Create(
+ OmxDataDecoder* aDataDecoder, OmxPromiseLayer* aPromiseLayer,
+ TaskQueue* aTaskQueue, layers::ImageContainer* aImageContainer) {
+ return nullptr;
+}
+
+#endif
+} // namespace mozilla
diff --git a/dom/media/platforms/omx/OmxPlatformLayer.h b/dom/media/platforms/omx/OmxPlatformLayer.h
new file mode 100644
index 0000000000..53f3dccbca
--- /dev/null
+++ b/dom/media/platforms/omx/OmxPlatformLayer.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(OmxPlatformLayer_h_)
+# define OmxPlatformLayer_h_
+
+# include "OMX_Core.h"
+# include "OMX_Types.h"
+# include "OMX_Video.h"
+
+# include "nsStringFwd.h"
+# include "OmxPromiseLayer.h"
+
+namespace mozilla {
+
+class TaskQueue;
+class TrackInfo;
+
+/*
+ * This class the the abstract layer of the platform OpenMax IL implementation.
+ *
+ * For some platform like andoird, it exposures its OpenMax IL via IOMX which
+ * is definitions are different comparing to standard.
+ * For other platforms like Raspberry Pi, it will be easy to implement this
+ * layer with the standard OpenMax IL api.
+ */
+class OmxPlatformLayer {
+ public:
+ typedef OmxPromiseLayer::BUFFERLIST BUFFERLIST;
+ typedef OmxPromiseLayer::BufferData BufferData;
+
+ virtual OMX_ERRORTYPE InitOmxToStateLoaded(const TrackInfo* aInfo) = 0;
+
+ OMX_ERRORTYPE Config();
+
+ virtual OMX_ERRORTYPE EmptyThisBuffer(BufferData* aData) = 0;
+
+ virtual OMX_ERRORTYPE FillThisBuffer(BufferData* aData) = 0;
+
+ virtual OMX_ERRORTYPE SendCommand(OMX_COMMANDTYPE aCmd, OMX_U32 aParam1,
+ OMX_PTR aCmdData) = 0;
+
+ // Buffer could be platform dependent. Therefore, derived class needs to
+ // implement its owned buffer allocate/release API according to its platform
+ // type.
+ virtual nsresult AllocateOmxBuffer(OMX_DIRTYPE aType,
+ BUFFERLIST* aBufferList) = 0;
+
+ virtual nsresult ReleaseOmxBuffer(OMX_DIRTYPE aType,
+ BUFFERLIST* aBufferList) = 0;
+
+ virtual OMX_ERRORTYPE GetState(OMX_STATETYPE* aType) = 0;
+
+ virtual OMX_ERRORTYPE GetParameter(OMX_INDEXTYPE aParamIndex,
+ OMX_PTR aComponentParameterStructure,
+ OMX_U32 aComponentParameterSize) = 0;
+
+ virtual OMX_ERRORTYPE SetParameter(OMX_INDEXTYPE nIndex,
+ OMX_PTR aComponentParameterStructure,
+ OMX_U32 aComponentParameterSize) = 0;
+
+ virtual nsresult Shutdown() = 0;
+
+ virtual ~OmxPlatformLayer() = default;
+
+ // For decoders, input port index is start port number and output port is
+ // next. See OpenMAX IL spec v1.1.2 section 8.6.1 & 8.8.1.
+ OMX_U32 InputPortIndex() { return mStartPortNumber; }
+
+ OMX_U32 OutputPortIndex() { return mStartPortNumber + 1; }
+
+ void GetPortIndices(nsTArray<uint32_t>& aPortIndex) {
+ aPortIndex.AppendElement(InputPortIndex());
+ aPortIndex.AppendElement(OutputPortIndex());
+ }
+
+ virtual OMX_VIDEO_CODINGTYPE CompressionFormat();
+
+ // Check if the platform implementation supports given MIME type.
+ static bool SupportsMimeType(const nsACString& aMimeType);
+
+ // Hide the details of creating implementation objects for different
+ // platforms.
+ static OmxPlatformLayer* Create(OmxDataDecoder* aDataDecoder,
+ OmxPromiseLayer* aPromiseLayer,
+ TaskQueue* aTaskQueue,
+ layers::ImageContainer* aImageContainer);
+
+ protected:
+ OmxPlatformLayer() : mInfo(nullptr), mStartPortNumber(0) {}
+
+ // The pointee is held by |OmxDataDecoder::mTrackInfo| and will outlive this
+ // pointer.
+ const TrackInfo* mInfo;
+ OMX_U32 mStartPortNumber;
+};
+
+} // namespace mozilla
+
+#endif // OmxPlatformLayer_h_
diff --git a/dom/media/platforms/omx/OmxPromiseLayer.cpp b/dom/media/platforms/omx/OmxPromiseLayer.cpp
new file mode 100644
index 0000000000..74d1fe15f8
--- /dev/null
+++ b/dom/media/platforms/omx/OmxPromiseLayer.cpp
@@ -0,0 +1,355 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OmxPromiseLayer.h"
+
+#include "ImageContainer.h"
+
+#include "OmxDataDecoder.h"
+#include "OmxPlatformLayer.h"
+
+#ifdef LOG
+# undef LOG
+#endif
+
+#define LOG(arg, ...) \
+ MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, \
+ ("OmxPromiseLayer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
+
+namespace mozilla {
+
+OmxPromiseLayer::OmxPromiseLayer(TaskQueue* aTaskQueue,
+ OmxDataDecoder* aDataDecoder,
+ layers::ImageContainer* aImageContainer)
+ : mTaskQueue(aTaskQueue) {
+ mPlatformLayer.reset(OmxPlatformLayer::Create(aDataDecoder, this, aTaskQueue,
+ aImageContainer));
+ MOZ_ASSERT(!!mPlatformLayer);
+}
+
+RefPtr<OmxPromiseLayer::OmxCommandPromise> OmxPromiseLayer::Init(
+ const TrackInfo* aInfo) {
+ MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+
+ OMX_ERRORTYPE err = mPlatformLayer->InitOmxToStateLoaded(aInfo);
+ if (err != OMX_ErrorNone) {
+ OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandStateSet);
+ return OmxCommandPromise::CreateAndReject(failure, __func__);
+ }
+
+ OMX_STATETYPE state = GetState();
+ if (state == OMX_StateLoaded) {
+ return OmxCommandPromise::CreateAndResolve(OMX_CommandStateSet, __func__);
+ }
+ if (state == OMX_StateIdle) {
+ return SendCommand(OMX_CommandStateSet, OMX_StateIdle, nullptr);
+ }
+
+ OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandStateSet);
+ return OmxCommandPromise::CreateAndReject(failure, __func__);
+}
+
+OMX_ERRORTYPE
+OmxPromiseLayer::Config() {
+ MOZ_ASSERT(GetState() == OMX_StateLoaded);
+
+ return mPlatformLayer->Config();
+}
+
+RefPtr<OmxPromiseLayer::OmxBufferPromise> OmxPromiseLayer::FillBuffer(
+ BufferData* aData) {
+ MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+ LOG("buffer %p", aData->mBuffer);
+
+ RefPtr<OmxBufferPromise> p = aData->mPromise.Ensure(__func__);
+
+ OMX_ERRORTYPE err = mPlatformLayer->FillThisBuffer(aData);
+
+ if (err != OMX_ErrorNone) {
+ OmxBufferFailureHolder failure(err, aData);
+ aData->mPromise.Reject(std::move(failure), __func__);
+ } else {
+ aData->mStatus = BufferData::BufferStatus::OMX_COMPONENT;
+ GetBufferHolders(OMX_DirOutput)->AppendElement(aData);
+ }
+
+ return p;
+}
+
+RefPtr<OmxPromiseLayer::OmxBufferPromise> OmxPromiseLayer::EmptyBuffer(
+ BufferData* aData) {
+ MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+ LOG("buffer %p, size %lu", aData->mBuffer, aData->mBuffer->nFilledLen);
+
+ RefPtr<OmxBufferPromise> p = aData->mPromise.Ensure(__func__);
+
+ OMX_ERRORTYPE err = mPlatformLayer->EmptyThisBuffer(aData);
+
+ if (err != OMX_ErrorNone) {
+ OmxBufferFailureHolder failure(err, aData);
+ aData->mPromise.Reject(std::move(failure), __func__);
+ } else {
+ if (aData->mRawData) {
+ mRawDatas.AppendElement(std::move(aData->mRawData));
+ }
+ aData->mStatus = BufferData::BufferStatus::OMX_COMPONENT;
+ GetBufferHolders(OMX_DirInput)->AppendElement(aData);
+ }
+
+ return p;
+}
+
+OmxPromiseLayer::BUFFERLIST* OmxPromiseLayer::GetBufferHolders(
+ OMX_DIRTYPE aType) {
+ MOZ_ASSERT(aType == OMX_DirInput || aType == OMX_DirOutput);
+
+ if (aType == OMX_DirInput) {
+ return &mInbufferHolders;
+ }
+
+ return &mOutbufferHolders;
+}
+
+already_AddRefed<MediaRawData> OmxPromiseLayer::FindAndRemoveRawData(
+ OMX_TICKS aTimecode) {
+ for (auto raw : mRawDatas) {
+ if (raw->mTime.ToMicroseconds() == aTimecode) {
+ mRawDatas.RemoveElement(raw);
+ return raw.forget();
+ }
+ }
+ return nullptr;
+}
+
+already_AddRefed<BufferData> OmxPromiseLayer::FindAndRemoveBufferHolder(
+ OMX_DIRTYPE aType, BufferData::BufferID aId) {
+ MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+
+ RefPtr<BufferData> holder;
+ BUFFERLIST* holders = GetBufferHolders(aType);
+
+ for (uint32_t i = 0; i < holders->Length(); i++) {
+ if (holders->ElementAt(i)->ID() == aId) {
+ holder = holders->ElementAt(i);
+ holders->RemoveElementAt(i);
+ return holder.forget();
+ }
+ }
+
+ return nullptr;
+}
+
+already_AddRefed<BufferData> OmxPromiseLayer::FindBufferById(
+ OMX_DIRTYPE aType, BufferData::BufferID aId) {
+ MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+
+ RefPtr<BufferData> holder;
+ BUFFERLIST* holders = GetBufferHolders(aType);
+
+ for (uint32_t i = 0; i < holders->Length(); i++) {
+ if (holders->ElementAt(i)->ID() == aId) {
+ holder = holders->ElementAt(i);
+ return holder.forget();
+ }
+ }
+
+ return nullptr;
+}
+
+void OmxPromiseLayer::EmptyFillBufferDone(OMX_DIRTYPE aType,
+ BufferData* aData) {
+ if (aData) {
+ LOG("type %d, buffer %p", aType, aData->mBuffer);
+ if (aType == OMX_DirOutput) {
+ aData->mRawData = nullptr;
+ aData->mRawData = FindAndRemoveRawData(aData->mBuffer->nTimeStamp);
+ }
+ aData->mStatus = BufferData::BufferStatus::OMX_CLIENT;
+ aData->mPromise.Resolve(aData, __func__);
+ } else {
+ LOG("type %d, no buffer", aType);
+ }
+}
+
+void OmxPromiseLayer::EmptyFillBufferDone(OMX_DIRTYPE aType,
+ BufferData::BufferID aID) {
+ RefPtr<BufferData> holder = FindAndRemoveBufferHolder(aType, aID);
+ EmptyFillBufferDone(aType, holder);
+}
+
+RefPtr<OmxPromiseLayer::OmxCommandPromise> OmxPromiseLayer::SendCommand(
+ OMX_COMMANDTYPE aCmd, OMX_U32 aParam1, OMX_PTR aCmdData) {
+ if (aCmd == OMX_CommandFlush) {
+ // It doesn't support another flush commands before previous one is
+ // completed.
+ MOZ_RELEASE_ASSERT(!mFlushCommands.Length());
+
+ // Some coomponents don't send event with OMX_ALL, they send flush complete
+ // event with input port and another event for output port.
+ // In prupose of better compatibility, we interpret the OMX_ALL to
+ // OMX_DirInput and OMX_DirOutput flush separately.
+ OMX_DIRTYPE types[] = {OMX_DIRTYPE::OMX_DirInput,
+ OMX_DIRTYPE::OMX_DirOutput};
+ for (const auto type : types) {
+ if ((aParam1 == type) || (aParam1 == OMX_ALL)) {
+ mFlushCommands.AppendElement(FlushCommand({type, aCmdData}));
+ }
+
+ if (type == OMX_DirInput) {
+ // Clear all buffered raw data.
+ mRawDatas.Clear();
+ }
+ }
+
+ // Don't overlay more than one flush command, some components can't overlay
+ // flush commands. So here we send another flush after receiving the
+ // previous flush completed event.
+ if (mFlushCommands.Length()) {
+ OMX_ERRORTYPE err = mPlatformLayer->SendCommand(
+ OMX_CommandFlush, mFlushCommands.ElementAt(0).type,
+ mFlushCommands.ElementAt(0).cmd);
+ if (err != OMX_ErrorNone) {
+ OmxCommandFailureHolder failure(OMX_ErrorNotReady, OMX_CommandFlush);
+ return OmxCommandPromise::CreateAndReject(failure, __func__);
+ }
+ } else {
+ LOG("OMX_CommandFlush parameter error");
+ OmxCommandFailureHolder failure(OMX_ErrorNotReady, OMX_CommandFlush);
+ return OmxCommandPromise::CreateAndReject(failure, __func__);
+ }
+ } else {
+ OMX_ERRORTYPE err = mPlatformLayer->SendCommand(aCmd, aParam1, aCmdData);
+ if (err != OMX_ErrorNone) {
+ OmxCommandFailureHolder failure(OMX_ErrorNotReady, aCmd);
+ return OmxCommandPromise::CreateAndReject(failure, __func__);
+ }
+ }
+
+ RefPtr<OmxCommandPromise> p;
+ if (aCmd == OMX_CommandStateSet) {
+ p = mCommandStatePromise.Ensure(__func__);
+ } else if (aCmd == OMX_CommandFlush) {
+ p = mFlushPromise.Ensure(__func__);
+ } else if (aCmd == OMX_CommandPortEnable) {
+ p = mPortEnablePromise.Ensure(__func__);
+ } else if (aCmd == OMX_CommandPortDisable) {
+ p = mPortDisablePromise.Ensure(__func__);
+ } else {
+ LOG("error unsupport command");
+ MOZ_ASSERT(0);
+ }
+
+ return p;
+}
+
+bool OmxPromiseLayer::Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1,
+ OMX_U32 aData2) {
+ OMX_COMMANDTYPE cmd = (OMX_COMMANDTYPE)aData1;
+ switch (aEvent) {
+ case OMX_EventCmdComplete: {
+ if (cmd == OMX_CommandStateSet) {
+ mCommandStatePromise.Resolve(OMX_CommandStateSet, __func__);
+ } else if (cmd == OMX_CommandFlush) {
+ MOZ_RELEASE_ASSERT(mFlushCommands.ElementAt(0).type == aData2);
+ LOG("OMX_CommandFlush completed port type %lu", aData2);
+ mFlushCommands.RemoveElementAt(0);
+
+ // Sending next flush command.
+ if (mFlushCommands.Length()) {
+ OMX_ERRORTYPE err = mPlatformLayer->SendCommand(
+ OMX_CommandFlush, mFlushCommands.ElementAt(0).type,
+ mFlushCommands.ElementAt(0).cmd);
+ if (err != OMX_ErrorNone) {
+ OmxCommandFailureHolder failure(OMX_ErrorNotReady,
+ OMX_CommandFlush);
+ mFlushPromise.Reject(failure, __func__);
+ }
+ } else {
+ mFlushPromise.Resolve(OMX_CommandFlush, __func__);
+ }
+ } else if (cmd == OMX_CommandPortDisable) {
+ mPortDisablePromise.Resolve(OMX_CommandPortDisable, __func__);
+ } else if (cmd == OMX_CommandPortEnable) {
+ mPortEnablePromise.Resolve(OMX_CommandPortEnable, __func__);
+ }
+ break;
+ }
+ case OMX_EventError: {
+ if (cmd == OMX_CommandStateSet) {
+ OmxCommandFailureHolder failure(OMX_ErrorUndefined,
+ OMX_CommandStateSet);
+ mCommandStatePromise.Reject(failure, __func__);
+ } else if (cmd == OMX_CommandFlush) {
+ OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandFlush);
+ mFlushPromise.Reject(failure, __func__);
+ } else if (cmd == OMX_CommandPortDisable) {
+ OmxCommandFailureHolder failure(OMX_ErrorUndefined,
+ OMX_CommandPortDisable);
+ mPortDisablePromise.Reject(failure, __func__);
+ } else if (cmd == OMX_CommandPortEnable) {
+ OmxCommandFailureHolder failure(OMX_ErrorUndefined,
+ OMX_CommandPortEnable);
+ mPortEnablePromise.Reject(failure, __func__);
+ } else {
+ return false;
+ }
+ break;
+ }
+ default: {
+ return false;
+ }
+ }
+ return true;
+}
+
+nsresult OmxPromiseLayer::AllocateOmxBuffer(OMX_DIRTYPE aType,
+ BUFFERLIST* aBuffers) {
+ return mPlatformLayer->AllocateOmxBuffer(aType, aBuffers);
+}
+
+nsresult OmxPromiseLayer::ReleaseOmxBuffer(OMX_DIRTYPE aType,
+ BUFFERLIST* aBuffers) {
+ return mPlatformLayer->ReleaseOmxBuffer(aType, aBuffers);
+}
+
+OMX_STATETYPE
+OmxPromiseLayer::GetState() {
+ OMX_STATETYPE state;
+ OMX_ERRORTYPE err = mPlatformLayer->GetState(&state);
+ return err == OMX_ErrorNone ? state : OMX_StateInvalid;
+}
+
+OMX_ERRORTYPE
+OmxPromiseLayer::GetParameter(OMX_INDEXTYPE aParamIndex,
+ OMX_PTR aComponentParameterStructure,
+ OMX_U32 aComponentParameterSize) {
+ return mPlatformLayer->GetParameter(aParamIndex, aComponentParameterStructure,
+ aComponentParameterSize);
+}
+
+OMX_ERRORTYPE
+OmxPromiseLayer::SetParameter(OMX_INDEXTYPE aParamIndex,
+ OMX_PTR aComponentParameterStructure,
+ OMX_U32 aComponentParameterSize) {
+ return mPlatformLayer->SetParameter(aParamIndex, aComponentParameterStructure,
+ aComponentParameterSize);
+}
+
+OMX_U32
+OmxPromiseLayer::InputPortIndex() { return mPlatformLayer->InputPortIndex(); }
+
+OMX_U32
+OmxPromiseLayer::OutputPortIndex() { return mPlatformLayer->OutputPortIndex(); }
+
+nsresult OmxPromiseLayer::Shutdown() {
+ LOG("");
+ MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+ MOZ_ASSERT(!GetBufferHolders(OMX_DirInput)->Length());
+ MOZ_ASSERT(!GetBufferHolders(OMX_DirOutput)->Length());
+ return mPlatformLayer->Shutdown();
+}
+
+} // namespace mozilla
diff --git a/dom/media/platforms/omx/OmxPromiseLayer.h b/dom/media/platforms/omx/OmxPromiseLayer.h
new file mode 100644
index 0000000000..82a1b3c268
--- /dev/null
+++ b/dom/media/platforms/omx/OmxPromiseLayer.h
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(OmxPromiseLayer_h_)
+# define OmxPromiseLayer_h_
+
+# include "mozilla/MozPromise.h"
+# include "mozilla/TaskQueue.h"
+
+# include "OMX_Core.h"
+# include "OMX_Types.h"
+
+namespace mozilla {
+
+namespace layers {
+class ImageContainer;
+}
+
+class MediaData;
+class MediaRawData;
+class OmxDataDecoder;
+class OmxPlatformLayer;
+class TrackInfo;
+
+/* This class acts as a middle layer between OmxDataDecoder and the underlying
+ * OmxPlatformLayer.
+ *
+ * This class has two purposes:
+ * 1. Using promise instead of OpenMax async callback function.
+ * For example, OmxCommandPromise is used for OpenMax IL SendCommand.
+ * 2. Manage the buffer exchanged between client and component.
+ * Because omx buffer works crossing threads, so each omx buffer has its own
+ * promise, it is defined in BufferData.
+ *
+ * All of functions and members in this class should be run in the same
+ * TaskQueue.
+ */
+class OmxPromiseLayer {
+ protected:
+ virtual ~OmxPromiseLayer() = default;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OmxPromiseLayer)
+
+ OmxPromiseLayer(TaskQueue* aTaskQueue, OmxDataDecoder* aDataDecoder,
+ layers::ImageContainer* aImageContainer);
+
+ class BufferData;
+
+ typedef nsTArray<RefPtr<BufferData>> BUFFERLIST;
+
+ class OmxBufferFailureHolder {
+ public:
+ OmxBufferFailureHolder(OMX_ERRORTYPE aError, BufferData* aBuffer)
+ : mError(aError), mBuffer(aBuffer) {}
+
+ OMX_ERRORTYPE mError;
+ BufferData* mBuffer;
+ };
+
+ typedef MozPromise<BufferData*, OmxBufferFailureHolder,
+ /* IsExclusive = */ false>
+ OmxBufferPromise;
+
+ class OmxCommandFailureHolder {
+ public:
+ OmxCommandFailureHolder(OMX_ERRORTYPE aErrorType,
+ OMX_COMMANDTYPE aCommandType)
+ : mErrorType(aErrorType), mCommandType(aCommandType) {}
+
+ OMX_ERRORTYPE mErrorType;
+ OMX_COMMANDTYPE mCommandType;
+ };
+
+ typedef MozPromise<OMX_COMMANDTYPE, OmxCommandFailureHolder,
+ /* IsExclusive = */ true>
+ OmxCommandPromise;
+
+ typedef MozPromise<uint32_t, bool, /* IsExclusive = */ true>
+ OmxPortConfigPromise;
+
+ // TODO: maybe a generic promise is good enough for this case?
+ RefPtr<OmxCommandPromise> Init(const TrackInfo* aInfo);
+
+ OMX_ERRORTYPE Config();
+
+ RefPtr<OmxBufferPromise> FillBuffer(BufferData* aData);
+
+ RefPtr<OmxBufferPromise> EmptyBuffer(BufferData* aData);
+
+ RefPtr<OmxCommandPromise> SendCommand(OMX_COMMANDTYPE aCmd, OMX_U32 aParam1,
+ OMX_PTR aCmdData);
+
+ nsresult AllocateOmxBuffer(OMX_DIRTYPE aType, BUFFERLIST* aBuffers);
+
+ nsresult ReleaseOmxBuffer(OMX_DIRTYPE aType, BUFFERLIST* aBuffers);
+
+ OMX_STATETYPE GetState();
+
+ OMX_ERRORTYPE GetParameter(OMX_INDEXTYPE aParamIndex,
+ OMX_PTR aComponentParameterStructure,
+ OMX_U32 aComponentParameterSize);
+
+ OMX_ERRORTYPE SetParameter(OMX_INDEXTYPE nIndex,
+ OMX_PTR aComponentParameterStructure,
+ OMX_U32 aComponentParameterSize);
+
+ OMX_U32 InputPortIndex();
+
+ OMX_U32 OutputPortIndex();
+
+ nsresult Shutdown();
+
+ // BufferData maintains the status of OMX buffer (OMX_BUFFERHEADERTYPE).
+ // mStatus tracks the buffer owner.
+ // And a promise because OMX buffer working among different threads.
+ class BufferData {
+ protected:
+ virtual ~BufferData() = default;
+
+ public:
+ explicit BufferData(OMX_BUFFERHEADERTYPE* aBuffer)
+ : mEos(false), mStatus(BufferStatus::FREE), mBuffer(aBuffer) {}
+
+ typedef void* BufferID;
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BufferData)
+
+ // In most cases, the ID of this buffer is the pointer address of mBuffer.
+ // However, on some platforms it may be another value.
+ virtual BufferID ID() { return mBuffer; }
+
+ // Return the platform dependent MediaData().
+ // For example, it returns the MediaData with Gralloc texture.
+ // If it returns nullptr, then caller uses the normal way to
+ // create MediaData().
+ virtual already_AddRefed<MediaData> GetPlatformMediaData() {
+ return nullptr;
+ }
+
+ // The buffer could be used by several objects. And only one object owns the
+ // buffer the same time.
+ // FREE:
+ // nobody uses it.
+ //
+ // OMX_COMPONENT:
+ // buffer is used by OMX component (OmxPlatformLayer).
+ //
+ // OMX_CLIENT:
+ // buffer is used by client which is wait for audio/video playing
+ // (OmxDataDecoder)
+ //
+ // OMX_CLIENT_OUTPUT:
+ // used by client to output decoded data (for example, Gecko layer in
+ // this case)
+ //
+ // For output port buffer, the status transition is:
+ // FREE -> OMX_COMPONENT -> OMX_CLIENT -> OMX_CLIENT_OUTPUT -> FREE
+ //
+ // For input port buffer, the status transition is:
+ // FREE -> OMX_COMPONENT -> OMX_CLIENT -> FREE
+ //
+ enum BufferStatus {
+ FREE,
+ OMX_COMPONENT,
+ OMX_CLIENT,
+ OMX_CLIENT_OUTPUT,
+ INVALID
+ };
+
+ bool mEos;
+
+ // The raw keeps in OmxPromiseLayer after EmptyBuffer and then passing to
+ // output decoded buffer in EmptyFillBufferDone. It is used to keep the
+ // records of the original data from demuxer, like duration, stream
+ // offset...etc.
+ RefPtr<MediaRawData> mRawData;
+
+ // Because OMX buffer works across threads, so it uses a promise
+ // for each buffer when the buffer is used by Omx component.
+ MozPromiseHolder<OmxBufferPromise> mPromise;
+ BufferStatus mStatus;
+ OMX_BUFFERHEADERTYPE* mBuffer;
+ };
+
+ void EmptyFillBufferDone(OMX_DIRTYPE aType, BufferData::BufferID aID);
+
+ void EmptyFillBufferDone(OMX_DIRTYPE aType, BufferData* aData);
+
+ already_AddRefed<BufferData> FindBufferById(OMX_DIRTYPE aType,
+ BufferData::BufferID aId);
+
+ already_AddRefed<BufferData> FindAndRemoveBufferHolder(
+ OMX_DIRTYPE aType, BufferData::BufferID aId);
+
+ // Return true if event is handled.
+ bool Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2);
+
+ protected:
+ struct FlushCommand {
+ OMX_DIRTYPE type;
+ OMX_PTR cmd;
+ };
+
+ BUFFERLIST* GetBufferHolders(OMX_DIRTYPE aType);
+
+ already_AddRefed<MediaRawData> FindAndRemoveRawData(OMX_TICKS aTimecode);
+
+ RefPtr<TaskQueue> mTaskQueue;
+
+ MozPromiseHolder<OmxCommandPromise> mCommandStatePromise;
+
+ MozPromiseHolder<OmxCommandPromise> mPortDisablePromise;
+
+ MozPromiseHolder<OmxCommandPromise> mPortEnablePromise;
+
+ MozPromiseHolder<OmxCommandPromise> mFlushPromise;
+
+ nsTArray<FlushCommand> mFlushCommands;
+
+ UniquePtr<OmxPlatformLayer> mPlatformLayer;
+
+ private:
+ // Elements are added to holders when FillBuffer() or FillBuffer(). And
+ // removing element when the promise is resolved. Buffers in these lists
+ // should NOT be used by other component; for example, output it to audio
+ // output. These lists should be empty when engine is about to shutdown.
+ //
+ // Note:
+ // There bufferlist should not be used by other class directly.
+ BUFFERLIST mInbufferHolders;
+
+ BUFFERLIST mOutbufferHolders;
+
+ nsTArray<RefPtr<MediaRawData>> mRawDatas;
+};
+
+} // namespace mozilla
+
+#endif /* OmxPromiseLayer_h_ */
diff --git a/dom/media/platforms/omx/PureOmxPlatformLayer.cpp b/dom/media/platforms/omx/PureOmxPlatformLayer.cpp
new file mode 100644
index 0000000000..dd6b259ac6
--- /dev/null
+++ b/dom/media/platforms/omx/PureOmxPlatformLayer.cpp
@@ -0,0 +1,405 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OmxDataDecoder.h"
+#include "OmxPromiseLayer.h"
+#include "PureOmxPlatformLayer.h"
+#include "OmxCoreLibLinker.h"
+
+#ifdef LOG
+# undef LOG
+#endif
+
+#define LOG(arg, ...) \
+ MOZ_LOG( \
+ sPDMLog, mozilla::LogLevel::Debug, \
+ ("PureOmxPlatformLayer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
+#define LOG_BUF(arg, ...) \
+ MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, \
+ ("PureOmxBufferData(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
+
+namespace mozilla {
+
+#define OMX_FUNC(func) extern typeof(func)* func;
+#include "OmxFunctionList.h"
+#undef OMX_FUNC
+
+PureOmxBufferData::PureOmxBufferData(
+ const PureOmxPlatformLayer& aPlatformLayer,
+ const OMX_PARAM_PORTDEFINITIONTYPE& aPortDef)
+ : BufferData(nullptr), mPlatformLayer(aPlatformLayer), mPortDef(aPortDef) {
+ LOG_BUF("");
+
+ if (ShouldUseEGLImage()) {
+ // TODO
+ LOG_BUF(
+ "OMX_UseEGLImage() seems available but using it isn't implemented "
+ "yet.");
+ }
+
+ OMX_ERRORTYPE err;
+ err = OMX_AllocateBuffer(mPlatformLayer.GetComponent(), &mBuffer,
+ mPortDef.nPortIndex, this, mPortDef.nBufferSize);
+ if (err != OMX_ErrorNone) {
+ LOG_BUF("Failed to allocate the buffer!: 0x%08x", err);
+ }
+}
+
+PureOmxBufferData::~PureOmxBufferData() {
+ LOG_BUF("");
+ ReleaseBuffer();
+}
+
+void PureOmxBufferData::ReleaseBuffer() {
+ LOG_BUF("");
+
+ if (mBuffer) {
+ OMX_ERRORTYPE err;
+ err = OMX_FreeBuffer(mPlatformLayer.GetComponent(), mPortDef.nPortIndex,
+ mBuffer);
+ if (err != OMX_ErrorNone) {
+ LOG_BUF("Failed to free the buffer!: 0x%08x", err);
+ }
+ mBuffer = nullptr;
+ }
+}
+
+bool PureOmxBufferData::ShouldUseEGLImage() {
+ OMX_ERRORTYPE err;
+ err = OMX_UseEGLImage(mPlatformLayer.GetComponent(), nullptr,
+ mPortDef.nPortIndex, nullptr, nullptr);
+ return (err != OMX_ErrorNotImplemented);
+}
+
+/* static */
+bool PureOmxPlatformLayer::Init(void) {
+ if (!OmxCoreLibLinker::Link()) {
+ return false;
+ }
+
+ OMX_ERRORTYPE err = OMX_Init();
+ if (err != OMX_ErrorNone) {
+ MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug,
+ ("PureOmxPlatformLayer::%s: Failed to initialize OMXCore: 0x%08x",
+ __func__, err));
+ return false;
+ }
+
+ return true;
+}
+
+/* static */
+OMX_CALLBACKTYPE PureOmxPlatformLayer::sCallbacks = {
+ EventHandler, EmptyBufferDone, FillBufferDone};
+
+PureOmxPlatformLayer::PureOmxPlatformLayer(
+ OmxDataDecoder* aDataDecoder, OmxPromiseLayer* aPromiseLayer,
+ TaskQueue* aTaskQueue, layers::ImageContainer* aImageContainer)
+ : mComponent(nullptr),
+ mDataDecoder(aDataDecoder),
+ mPromiseLayer(aPromiseLayer),
+ mTaskQueue(aTaskQueue),
+ mImageContainer(aImageContainer) {
+ LOG("");
+}
+
+PureOmxPlatformLayer::~PureOmxPlatformLayer() { LOG(""); }
+
+OMX_ERRORTYPE
+PureOmxPlatformLayer::InitOmxToStateLoaded(const TrackInfo* aInfo) {
+ LOG("");
+
+ if (!aInfo) {
+ return OMX_ErrorUndefined;
+ }
+ mInfo = aInfo;
+
+ return CreateComponent();
+}
+
+OMX_ERRORTYPE
+PureOmxPlatformLayer::EmptyThisBuffer(BufferData* aData) {
+ LOG("");
+ return OMX_EmptyThisBuffer(mComponent, aData->mBuffer);
+}
+
+OMX_ERRORTYPE
+PureOmxPlatformLayer::FillThisBuffer(BufferData* aData) {
+ LOG("");
+ return OMX_FillThisBuffer(mComponent, aData->mBuffer);
+}
+
+OMX_ERRORTYPE
+PureOmxPlatformLayer::SendCommand(OMX_COMMANDTYPE aCmd, OMX_U32 aParam1,
+ OMX_PTR aCmdData) {
+ LOG("aCmd: 0x%08x", aCmd);
+ if (!mComponent) {
+ return OMX_ErrorUndefined;
+ }
+ return OMX_SendCommand(mComponent, aCmd, aParam1, aCmdData);
+}
+
+nsresult PureOmxPlatformLayer::FindPortDefinition(
+ OMX_DIRTYPE aType, OMX_PARAM_PORTDEFINITIONTYPE& portDef) {
+ nsTArray<uint32_t> portIndex;
+ GetPortIndices(portIndex);
+ for (auto idx : portIndex) {
+ InitOmxParameter(&portDef);
+ portDef.nPortIndex = idx;
+
+ OMX_ERRORTYPE err;
+ err = GetParameter(OMX_IndexParamPortDefinition, &portDef,
+ sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ if (err != OMX_ErrorNone) {
+ return NS_ERROR_FAILURE;
+ } else if (portDef.eDir == aType) {
+ LOG("Found OMX_IndexParamPortDefinition: port: %d, type: %d",
+ portDef.nPortIndex, portDef.eDir);
+ return NS_OK;
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+nsresult PureOmxPlatformLayer::AllocateOmxBuffer(OMX_DIRTYPE aType,
+ BUFFERLIST* aBufferList) {
+ LOG("aType: %d", aType);
+
+ OMX_PARAM_PORTDEFINITIONTYPE portDef;
+ nsresult result = FindPortDefinition(aType, portDef);
+ if (result != NS_OK) {
+ return result;
+ }
+
+ LOG("nBufferCountActual: %d, nBufferSize: %d", portDef.nBufferCountActual,
+ portDef.nBufferSize);
+
+ for (OMX_U32 i = 0; i < portDef.nBufferCountActual; ++i) {
+ RefPtr<PureOmxBufferData> buffer = new PureOmxBufferData(*this, portDef);
+ aBufferList->AppendElement(buffer);
+ }
+
+ return NS_OK;
+}
+
+nsresult PureOmxPlatformLayer::ReleaseOmxBuffer(OMX_DIRTYPE aType,
+ BUFFERLIST* aBufferList) {
+ LOG("aType: 0x%08x", aType);
+
+ uint32_t len = aBufferList->Length();
+ for (uint32_t i = 0; i < len; i++) {
+ PureOmxBufferData* buffer =
+ static_cast<PureOmxBufferData*>(aBufferList->ElementAt(i).get());
+
+ // All raw OpenMAX buffers have to be released here to flush
+ // OMX_CommandStateSet for switching the state to OMX_StateLoaded.
+ // See OmxDataDecoder::DoAsyncShutdown() for more detail.
+ buffer->ReleaseBuffer();
+ }
+ aBufferList->Clear();
+
+ return NS_OK;
+}
+
+OMX_ERRORTYPE
+PureOmxPlatformLayer::GetState(OMX_STATETYPE* aType) {
+ LOG("");
+
+ if (mComponent) {
+ return OMX_GetState(mComponent, aType);
+ }
+
+ return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE
+PureOmxPlatformLayer::GetParameter(OMX_INDEXTYPE aParamIndex,
+ OMX_PTR aComponentParameterStructure,
+ OMX_U32 aComponentParameterSize) {
+ LOG("aParamIndex: 0x%08x", aParamIndex);
+
+ if (!mComponent) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_GetParameter(mComponent, aParamIndex,
+ aComponentParameterStructure);
+}
+
+OMX_ERRORTYPE
+PureOmxPlatformLayer::SetParameter(OMX_INDEXTYPE aParamIndex,
+ OMX_PTR aComponentParameterStructure,
+ OMX_U32 aComponentParameterSize) {
+ LOG("aParamIndex: 0x%08x", aParamIndex);
+
+ if (!mComponent) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_SetParameter(mComponent, aParamIndex,
+ aComponentParameterStructure);
+}
+
+nsresult PureOmxPlatformLayer::Shutdown() {
+ LOG("");
+ if (mComponent) {
+ OMX_FreeHandle(mComponent);
+ mComponent = nullptr;
+ }
+ mPromiseLayer = nullptr;
+ mDataDecoder = nullptr;
+ return NS_OK;
+}
+
+/* static */
+OMX_ERRORTYPE PureOmxPlatformLayer::EventHandler(OMX_HANDLETYPE hComponent,
+ OMX_PTR pAppData,
+ OMX_EVENTTYPE eEventType,
+ OMX_U32 nData1, OMX_U32 nData2,
+ OMX_PTR pEventData) {
+ PureOmxPlatformLayer* self = static_cast<PureOmxPlatformLayer*>(pAppData);
+ nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+ "mozilla::PureOmxPlatformLayer::EventHandler",
+ [self, eEventType, nData1, nData2, pEventData]() {
+ self->EventHandler(eEventType, nData1, nData2, pEventData);
+ });
+ nsresult rv = self->mTaskQueue->Dispatch(r.forget());
+ return NS_SUCCEEDED(rv) ? OMX_ErrorNone : OMX_ErrorUndefined;
+}
+
+/* static */
+OMX_ERRORTYPE PureOmxPlatformLayer::EmptyBufferDone(
+ OMX_HANDLETYPE hComponent, OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
+ PureOmxPlatformLayer* self = static_cast<PureOmxPlatformLayer*>(pAppData);
+ nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+ "mozilla::PureOmxPlatformLayer::EmptyBufferDone",
+ [self, pBuffer]() { self->EmptyBufferDone(pBuffer); });
+ nsresult rv = self->mTaskQueue->Dispatch(r.forget());
+ return NS_SUCCEEDED(rv) ? OMX_ErrorNone : OMX_ErrorUndefined;
+}
+
+/* static */
+OMX_ERRORTYPE PureOmxPlatformLayer::FillBufferDone(
+ OMX_OUT OMX_HANDLETYPE hComponent, OMX_OUT OMX_PTR pAppData,
+ OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) {
+ PureOmxPlatformLayer* self = static_cast<PureOmxPlatformLayer*>(pAppData);
+ nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+ "mozilla::PureOmxPlatformLayer::FillBufferDone",
+ [self, pBuffer]() { self->FillBufferDone(pBuffer); });
+ nsresult rv = self->mTaskQueue->Dispatch(r.forget());
+ return NS_SUCCEEDED(rv) ? OMX_ErrorNone : OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE
+PureOmxPlatformLayer::EventHandler(OMX_EVENTTYPE eEventType, OMX_U32 nData1,
+ OMX_U32 nData2, OMX_PTR pEventData) {
+ bool handled = mPromiseLayer->Event(eEventType, nData1, nData2);
+ LOG("eEventType: 0x%08x, handled: %d", eEventType, handled);
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE
+PureOmxPlatformLayer::EmptyBufferDone(OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
+ PureOmxBufferData* buffer =
+ static_cast<PureOmxBufferData*>(pBuffer->pAppPrivate);
+ OMX_DIRTYPE portDirection = buffer->GetPortDirection();
+ LOG("PortDirection: %d", portDirection);
+ mPromiseLayer->EmptyFillBufferDone(portDirection, buffer);
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE
+PureOmxPlatformLayer::FillBufferDone(OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) {
+ PureOmxBufferData* buffer =
+ static_cast<PureOmxBufferData*>(pBuffer->pAppPrivate);
+ OMX_DIRTYPE portDirection = buffer->GetPortDirection();
+ LOG("PortDirection: %d", portDirection);
+ mPromiseLayer->EmptyFillBufferDone(portDirection, buffer);
+ return OMX_ErrorNone;
+}
+
+bool PureOmxPlatformLayer::SupportsMimeType(const nsACString& aMimeType) {
+ return FindStandardComponent(aMimeType, nullptr);
+}
+
+static bool GetStandardComponentRole(const nsACString& aMimeType,
+ nsACString& aRole) {
+ if (aMimeType.EqualsLiteral("video/avc") ||
+ aMimeType.EqualsLiteral("video/mp4") ||
+ aMimeType.EqualsLiteral("video/mp4v-es")) {
+ aRole.Assign("video_decoder.avc");
+ return true;
+ } else if (aMimeType.EqualsLiteral("audio/mp4a-latm") ||
+ aMimeType.EqualsLiteral("audio/mp4") ||
+ aMimeType.EqualsLiteral("audio/aac")) {
+ aRole.Assign("audio_decoder.aac");
+ return true;
+ }
+ return false;
+}
+
+/* static */
+bool PureOmxPlatformLayer::FindStandardComponent(const nsACString& aMimeType,
+ nsACString* aComponentName) {
+ nsAutoCString role;
+ if (!GetStandardComponentRole(aMimeType, role)) return false;
+
+ OMX_U32 nComponents = 0;
+ OMX_ERRORTYPE err;
+ err = OMX_GetComponentsOfRole(const_cast<OMX_STRING>(role.Data()),
+ &nComponents, nullptr);
+ if (err != OMX_ErrorNone || nComponents <= 0) {
+ return false;
+ }
+ if (!aComponentName) {
+ return true;
+ }
+
+ // TODO:
+ // Only the first component will be used.
+ // We should detect the most preferred component.
+ OMX_U8* componentNames[1];
+ UniquePtr<OMX_U8[]> componentName;
+ componentName = MakeUniqueFallible<OMX_U8[]>(OMX_MAX_STRINGNAME_SIZE);
+ componentNames[0] = componentName.get();
+ nComponents = 1;
+ err = OMX_GetComponentsOfRole(const_cast<OMX_STRING>(role.Data()),
+ &nComponents, componentNames);
+ if (err == OMX_ErrorNone) {
+ MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug,
+ ("PureOmxPlatformLayer::%s: A component has been found for %s: %s",
+ __func__, aMimeType.Data(), componentNames[0]));
+ aComponentName->Assign(reinterpret_cast<char*>(componentNames[0]));
+ }
+
+ return err == OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE
+PureOmxPlatformLayer::CreateComponent(const nsACString* aComponentName) {
+ nsAutoCString componentName;
+ if (aComponentName) {
+ componentName = *aComponentName;
+ } else if (!FindStandardComponent(mInfo->mMimeType, &componentName)) {
+ return OMX_ErrorComponentNotFound;
+ }
+
+ OMX_ERRORTYPE err;
+ err = OMX_GetHandle(&mComponent, const_cast<OMX_STRING>(componentName.Data()),
+ this, &sCallbacks);
+
+ const char* mime = mInfo->mMimeType.Data();
+ if (err == OMX_ErrorNone) {
+ LOG("Succeeded to create the component for %s", mime);
+ } else {
+ LOG("Failed to create the component for %s: 0x%08x", mime, err);
+ }
+
+ return err;
+}
+
+} // namespace mozilla
diff --git a/dom/media/platforms/omx/PureOmxPlatformLayer.h b/dom/media/platforms/omx/PureOmxPlatformLayer.h
new file mode 100644
index 0000000000..95c95b8781
--- /dev/null
+++ b/dom/media/platforms/omx/PureOmxPlatformLayer.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(PureOmxPlatformLayer_h_)
+# define PureOmxPlatformLayer_h_
+
+# include "OmxPlatformLayer.h"
+
+namespace mozilla {
+
+class PureOmxPlatformLayer;
+
+class PureOmxBufferData : public OmxPromiseLayer::BufferData {
+ protected:
+ virtual ~PureOmxBufferData();
+
+ public:
+ PureOmxBufferData(const PureOmxPlatformLayer& aPlatformLayer,
+ const OMX_PARAM_PORTDEFINITIONTYPE& aPortDef);
+
+ void ReleaseBuffer();
+ OMX_DIRTYPE GetPortDirection() const { return mPortDef.eDir; };
+
+ protected:
+ bool ShouldUseEGLImage();
+
+ const PureOmxPlatformLayer& mPlatformLayer;
+ const OMX_PARAM_PORTDEFINITIONTYPE mPortDef;
+};
+
+class PureOmxPlatformLayer : public OmxPlatformLayer {
+ public:
+ static bool Init(void);
+
+ static bool SupportsMimeType(const nsACString& aMimeType);
+
+ PureOmxPlatformLayer(OmxDataDecoder* aDataDecoder,
+ OmxPromiseLayer* aPromiseLayer, TaskQueue* aTaskQueue,
+ layers::ImageContainer* aImageContainer);
+
+ virtual ~PureOmxPlatformLayer();
+
+ OMX_ERRORTYPE InitOmxToStateLoaded(const TrackInfo* aInfo) override;
+
+ OMX_ERRORTYPE EmptyThisBuffer(BufferData* aData) override;
+
+ OMX_ERRORTYPE FillThisBuffer(BufferData* aData) override;
+
+ OMX_ERRORTYPE SendCommand(OMX_COMMANDTYPE aCmd, OMX_U32 aParam1,
+ OMX_PTR aCmdData) override;
+
+ nsresult AllocateOmxBuffer(OMX_DIRTYPE aType,
+ BUFFERLIST* aBufferList) override;
+
+ nsresult ReleaseOmxBuffer(OMX_DIRTYPE aType,
+ BUFFERLIST* aBufferList) override;
+
+ OMX_ERRORTYPE GetState(OMX_STATETYPE* aType) override;
+
+ OMX_ERRORTYPE GetParameter(OMX_INDEXTYPE aParamIndex,
+ OMX_PTR aComponentParameterStructure,
+ OMX_U32 aComponentParameterSize) override;
+
+ OMX_ERRORTYPE SetParameter(OMX_INDEXTYPE aParamIndex,
+ OMX_PTR aComponentParameterStructure,
+ OMX_U32 aComponentParameterSize) override;
+
+ nsresult Shutdown() override;
+
+ OMX_HANDLETYPE GetComponent() const { return mComponent; };
+
+ static OMX_ERRORTYPE EventHandler(OMX_HANDLETYPE hComponent, OMX_PTR pAppData,
+ OMX_EVENTTYPE eEventType, OMX_U32 nData1,
+ OMX_U32 nData2, OMX_PTR pEventData);
+ static OMX_ERRORTYPE EmptyBufferDone(OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
+ static OMX_ERRORTYPE FillBufferDone(OMX_OUT OMX_HANDLETYPE hComponent,
+ OMX_OUT OMX_PTR pAppData,
+ OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer);
+
+ protected:
+ static bool FindStandardComponent(const nsACString& aMimeType,
+ nsACString* aComponentName);
+
+ OMX_ERRORTYPE CreateComponent(const nsACString* aComponentName = nullptr);
+ nsresult FindPortDefinition(OMX_DIRTYPE aType,
+ OMX_PARAM_PORTDEFINITIONTYPE& portDef);
+
+ OMX_ERRORTYPE EventHandler(OMX_EVENTTYPE eEventType, OMX_U32 nData1,
+ OMX_U32 nData2, OMX_PTR pEventData);
+ OMX_ERRORTYPE EmptyBufferDone(OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
+ OMX_ERRORTYPE FillBufferDone(OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer);
+
+ protected:
+ static OMX_CALLBACKTYPE sCallbacks;
+
+ OMX_HANDLETYPE mComponent;
+ RefPtr<OmxDataDecoder> mDataDecoder;
+ RefPtr<OmxPromiseLayer> mPromiseLayer;
+ RefPtr<TaskQueue> mTaskQueue;
+ RefPtr<layers::ImageContainer> mImageContainer;
+};
+
+} // namespace mozilla
+
+#endif // PureOmxPlatformLayer_h_
diff --git a/dom/media/platforms/omx/moz.build b/dom/media/platforms/omx/moz.build
new file mode 100644
index 0000000000..c8a79cabb4
--- /dev/null
+++ b/dom/media/platforms/omx/moz.build
@@ -0,0 +1,36 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS += [
+ "OmxDecoderModule.h",
+]
+
+UNIFIED_SOURCES += [
+ "OmxDataDecoder.cpp",
+ "OmxDecoderModule.cpp",
+ "OmxPlatformLayer.cpp",
+ "OmxPromiseLayer.cpp",
+]
+
+LOCAL_INCLUDES += [
+ "/media/openmax_il/il112",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+if CONFIG["MOZ_OMX"]:
+ UNIFIED_SOURCES += [
+ "PureOmxPlatformLayer.cpp",
+ ]
+
+FINAL_LIBRARY = "xul"
+
+# Avoid warnings from third-party code that we can not modify.
+if CONFIG["CC_TYPE"] == "clang-cl":
+ CXXFLAGS += ["-Wno-invalid-source-encoding"]
+
+# Add libFuzzer configuration directives
+include("/tools/fuzzing/libfuzzer-config.mozbuild")