mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-08 00:07:30 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
667
dom/media/platforms/omx/GonkOmxPlatformLayer.cpp
Normal file
667
dom/media/platforms/omx/GonkOmxPlatformLayer.cpp
Normal file
|
|
@ -0,0 +1,667 @@
|
|||
/* -*- 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 "GonkOmxPlatformLayer.h"
|
||||
|
||||
#include <binder/MemoryDealer.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <media/IOMX.h>
|
||||
#include <media/stagefright/MediaCodecList.h>
|
||||
#include <utils/List.h>
|
||||
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/layers/TextureClient.h"
|
||||
#include "mozilla/layers/GrallocTextureClient.h"
|
||||
#include "mozilla/layers/ImageBridgeChild.h"
|
||||
#include "mozilla/layers/TextureClientRecycleAllocator.h"
|
||||
|
||||
#include "ImageContainer.h"
|
||||
#include "MediaInfo.h"
|
||||
#include "OmxDataDecoder.h"
|
||||
|
||||
|
||||
#ifdef LOG
|
||||
#undef LOG
|
||||
#endif
|
||||
|
||||
#define LOG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("GonkOmxPlatformLayer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
|
||||
|
||||
#define CHECK_ERR(err) \
|
||||
if (err != OK) { \
|
||||
LOG("error %d at %s", err, __func__); \
|
||||
return NS_ERROR_FAILURE; \
|
||||
} \
|
||||
|
||||
// Android proprietary value.
|
||||
#define ANDROID_OMX_VIDEO_CodingVP8 (static_cast<OMX_VIDEO_CODINGTYPE>(9))
|
||||
|
||||
using namespace android;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// In Gonk, the software component name has prefix "OMX.google". It needs to
|
||||
// have a way to use hardware codec first.
|
||||
bool IsSoftwareCodec(const char* aComponentName)
|
||||
{
|
||||
nsAutoCString str(aComponentName);
|
||||
return (str.Find(NS_LITERAL_CSTRING("OMX.google.")) == -1 ? false : true);
|
||||
}
|
||||
|
||||
bool IsInEmulator()
|
||||
{
|
||||
char propQemu[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.kernel.qemu", propQemu, "");
|
||||
return !strncmp(propQemu, "1", 1);
|
||||
}
|
||||
|
||||
class GonkOmxObserver : public BnOMXObserver {
|
||||
public:
|
||||
void onMessage(const omx_message& aMsg)
|
||||
{
|
||||
switch (aMsg.type) {
|
||||
case omx_message::EVENT:
|
||||
{
|
||||
sp<GonkOmxObserver> self = this;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aMsg] () {
|
||||
if (self->mClient && self->mClient->Event(aMsg.u.event_data.event,
|
||||
aMsg.u.event_data.data1,
|
||||
aMsg.u.event_data.data2))
|
||||
{
|
||||
return;
|
||||
}
|
||||
});
|
||||
mTaskQueue->Dispatch(r.forget());
|
||||
break;
|
||||
}
|
||||
case omx_message::EMPTY_BUFFER_DONE:
|
||||
{
|
||||
sp<GonkOmxObserver> self = this;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aMsg] () {
|
||||
if (!self->mPromiseLayer) {
|
||||
return;
|
||||
}
|
||||
BufferData::BufferID id = (BufferData::BufferID)aMsg.u.buffer_data.buffer;
|
||||
self->mPromiseLayer->EmptyFillBufferDone(OMX_DirInput, id);
|
||||
});
|
||||
mTaskQueue->Dispatch(r.forget());
|
||||
break;
|
||||
}
|
||||
case omx_message::FILL_BUFFER_DONE:
|
||||
{
|
||||
sp<GonkOmxObserver> self = this;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aMsg] () {
|
||||
if (!self->mPromiseLayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: these codes look a little ugly, it'd be better to improve them.
|
||||
RefPtr<BufferData> buf;
|
||||
BufferData::BufferID id = (BufferData::BufferID)aMsg.u.extended_buffer_data.buffer;
|
||||
buf = self->mPromiseLayer->FindAndRemoveBufferHolder(OMX_DirOutput, id);
|
||||
MOZ_RELEASE_ASSERT(buf);
|
||||
GonkBufferData* gonkBuffer = static_cast<GonkBufferData*>(buf.get());
|
||||
|
||||
// Copy the critical information to local buffer.
|
||||
if (gonkBuffer->IsLocalBuffer()) {
|
||||
gonkBuffer->mBuffer->nOffset = aMsg.u.extended_buffer_data.range_offset;
|
||||
gonkBuffer->mBuffer->nFilledLen = aMsg.u.extended_buffer_data.range_length;
|
||||
gonkBuffer->mBuffer->nFlags = aMsg.u.extended_buffer_data.flags;
|
||||
gonkBuffer->mBuffer->nTimeStamp = aMsg.u.extended_buffer_data.timestamp;
|
||||
}
|
||||
self->mPromiseLayer->EmptyFillBufferDone(OMX_DirOutput, buf);
|
||||
});
|
||||
mTaskQueue->Dispatch(r.forget());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
LOG("Unhandle event %d", aMsg.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
mPromiseLayer = nullptr;
|
||||
mClient = nullptr;
|
||||
}
|
||||
|
||||
GonkOmxObserver(TaskQueue* aTaskQueue, OmxPromiseLayer* aPromiseLayer, OmxDataDecoder* aDataDecoder)
|
||||
: mTaskQueue(aTaskQueue)
|
||||
, mPromiseLayer(aPromiseLayer)
|
||||
, mClient(aDataDecoder)
|
||||
{}
|
||||
|
||||
protected:
|
||||
RefPtr<TaskQueue> mTaskQueue;
|
||||
// TODO:
|
||||
// we should combine both event handlers into one. And we should provide
|
||||
// an unified way for event handling in OmxPlatformLayer class.
|
||||
RefPtr<OmxPromiseLayer> mPromiseLayer;
|
||||
RefPtr<OmxDataDecoder> mClient;
|
||||
};
|
||||
|
||||
// This class allocates Gralloc buffer and manages TextureClient's recycle.
|
||||
class GonkTextureClientRecycleHandler : public layers::ITextureClientRecycleAllocator
|
||||
{
|
||||
typedef MozPromise<layers::TextureClient*, nsresult, /* IsExclusive = */ true> TextureClientRecyclePromise;
|
||||
|
||||
public:
|
||||
GonkTextureClientRecycleHandler(OMX_VIDEO_PORTDEFINITIONTYPE& aDef)
|
||||
: ITextureClientRecycleAllocator()
|
||||
, mMonitor("GonkTextureClientRecycleHandler")
|
||||
{
|
||||
RefPtr<layers::ImageBridgeChild> bridge = layers::ImageBridgeChild::GetSingleton();
|
||||
|
||||
// Allocate Gralloc texture memory.
|
||||
layers::GrallocTextureData* textureData =
|
||||
layers::GrallocTextureData::Create(gfx::IntSize(aDef.nFrameWidth, aDef.nFrameHeight),
|
||||
aDef.eColorFormat,
|
||||
gfx::BackendType::NONE,
|
||||
GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_READ_OFTEN,
|
||||
bridge);
|
||||
|
||||
mGraphBuffer = textureData->GetGraphicBuffer();
|
||||
MOZ_ASSERT(mGraphBuffer.get());
|
||||
|
||||
mTextureClient =
|
||||
layers::TextureClient::CreateWithData(textureData,
|
||||
layers::TextureFlags::DEALLOCATE_CLIENT | layers::TextureFlags::RECYCLE,
|
||||
bridge);
|
||||
MOZ_ASSERT(mTextureClient);
|
||||
|
||||
mPromise.SetMonitor(&mMonitor);
|
||||
}
|
||||
|
||||
RefPtr<TextureClientRecyclePromise> WaitforRecycle()
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
MOZ_ASSERT(!!mGraphBuffer.get());
|
||||
|
||||
mTextureClient->SetRecycleAllocator(this);
|
||||
return mPromise.Ensure(__func__);
|
||||
}
|
||||
|
||||
// DO NOT use smart pointer to receive TextureClient; otherwise it will
|
||||
// distrupt the reference count.
|
||||
layers::TextureClient* GetTextureClient()
|
||||
{
|
||||
return mTextureClient;
|
||||
}
|
||||
|
||||
GraphicBuffer* GetGraphicBuffer()
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
return mGraphBuffer.get();
|
||||
}
|
||||
|
||||
// This function is called from layers thread.
|
||||
void RecycleTextureClient(layers::TextureClient* aClient) override
|
||||
{
|
||||
MOZ_ASSERT(mTextureClient == aClient);
|
||||
|
||||
// Clearing the recycle allocator drops a reference, so make sure we stay alive
|
||||
// for the duration of this function.
|
||||
RefPtr<GonkTextureClientRecycleHandler> kungFuDeathGrip(this);
|
||||
aClient->SetRecycleAllocator(nullptr);
|
||||
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
mPromise.ResolveIfExists(mTextureClient, __func__);
|
||||
}
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
|
||||
mPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
|
||||
|
||||
// DO NOT clear TextureClient here.
|
||||
// The ref count could be 1 and RecycleCallback will be called if we clear
|
||||
// the ref count here. That breaks the whole mechanism. (RecycleCallback
|
||||
// should be called from layers)
|
||||
mGraphBuffer = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
// Because TextureClient calls RecycleCallbackl when ref count is 1, so we
|
||||
// should hold only one reference here and use raw pointer when out of this
|
||||
// class.
|
||||
RefPtr<layers::TextureClient> mTextureClient;
|
||||
|
||||
// It is protected by mMonitor.
|
||||
sp<android::GraphicBuffer> mGraphBuffer;
|
||||
|
||||
// It is protected by mMonitor.
|
||||
MozPromiseHolder<TextureClientRecyclePromise> mPromise;
|
||||
|
||||
Monitor mMonitor;
|
||||
};
|
||||
|
||||
GonkBufferData::GonkBufferData(bool aLiveInLocal,
|
||||
GonkOmxPlatformLayer* aGonkPlatformLayer)
|
||||
: BufferData(nullptr)
|
||||
, mId(0)
|
||||
, mGonkPlatformLayer(aGonkPlatformLayer)
|
||||
{
|
||||
if (!aLiveInLocal) {
|
||||
mMirrorBuffer = new OMX_BUFFERHEADERTYPE;
|
||||
PodZero(mMirrorBuffer.get());
|
||||
mBuffer = mMirrorBuffer.get();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GonkBufferData::ReleaseBuffer()
|
||||
{
|
||||
if (mTextureClientRecycleHandler) {
|
||||
mTextureClientRecycleHandler->Shutdown();
|
||||
mTextureClientRecycleHandler = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkBufferData::InitSharedMemory(android::IMemory* aMemory)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mMirrorBuffer.get());
|
||||
|
||||
// aMemory is a IPC memory, it is safe to use it here.
|
||||
mBuffer->pBuffer = (OMX_U8*)aMemory->pointer();
|
||||
mBuffer->nAllocLen = aMemory->size();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkBufferData::InitLocalBuffer(IOMX::buffer_id aId)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!mMirrorBuffer.get());
|
||||
|
||||
mBuffer = (OMX_BUFFERHEADERTYPE*)aId;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkBufferData::InitGraphicBuffer(OMX_VIDEO_PORTDEFINITIONTYPE& aDef)
|
||||
{
|
||||
mTextureClientRecycleHandler = new GonkTextureClientRecycleHandler(aDef);
|
||||
|
||||
if (!mTextureClientRecycleHandler->GetGraphicBuffer()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<MediaData>
|
||||
GonkBufferData::GetPlatformMediaData()
|
||||
{
|
||||
if (mGonkPlatformLayer->GetTrackInfo()->GetAsAudioInfo()) {
|
||||
// This is audio decoding.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!mTextureClientRecycleHandler) {
|
||||
// There is no GraphicBuffer, it should fallback to normal YUV420 VideoData.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VideoInfo info(*mGonkPlatformLayer->GetTrackInfo()->GetAsVideoInfo());
|
||||
RefPtr<VideoData> data =
|
||||
VideoData::CreateAndCopyIntoTextureClient(info,
|
||||
0,
|
||||
mBuffer->nTimeStamp,
|
||||
1,
|
||||
mTextureClientRecycleHandler->GetTextureClient(),
|
||||
false,
|
||||
0,
|
||||
info.ImageRect());
|
||||
LOG("%p, disp width %d, height %d, pic width %d, height %d, time %ld",
|
||||
this, info.mDisplay.width, info.mDisplay.height,
|
||||
info.mImage.width, info.mImage.height, mBuffer->nTimeStamp);
|
||||
|
||||
// Get TextureClient Promise here to wait for resolved.
|
||||
RefPtr<GonkBufferData> self(this);
|
||||
mTextureClientRecycleHandler->WaitforRecycle()
|
||||
->Then(mGonkPlatformLayer->GetTaskQueue(), __func__,
|
||||
[self] () {
|
||||
self->mPromise.ResolveIfExists(self, __func__);
|
||||
},
|
||||
[self] () {
|
||||
OmxBufferFailureHolder failure(OMX_ErrorUndefined, self);
|
||||
self->mPromise.RejectIfExists(failure, __func__);
|
||||
});
|
||||
|
||||
return data.forget();
|
||||
}
|
||||
|
||||
GonkOmxPlatformLayer::GonkOmxPlatformLayer(OmxDataDecoder* aDataDecoder,
|
||||
OmxPromiseLayer* aPromiseLayer,
|
||||
TaskQueue* aTaskQueue,
|
||||
layers::ImageContainer* aImageContainer)
|
||||
: mTaskQueue(aTaskQueue)
|
||||
, mImageContainer(aImageContainer)
|
||||
, mNode(0)
|
||||
{
|
||||
mOmxObserver = new GonkOmxObserver(mTaskQueue, aPromiseLayer, aDataDecoder);
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkOmxPlatformLayer::AllocateOmxBuffer(OMX_DIRTYPE aType,
|
||||
BUFFERLIST* aBufferList)
|
||||
{
|
||||
MOZ_ASSERT(!mMemoryDealer[aType].get());
|
||||
|
||||
// Get port definition.
|
||||
OMX_PARAM_PORTDEFINITIONTYPE def;
|
||||
nsTArray<uint32_t> portindex;
|
||||
GetPortIndices(portindex);
|
||||
for (auto idx : portindex) {
|
||||
InitOmxParameter(&def);
|
||||
def.nPortIndex = idx;
|
||||
|
||||
OMX_ERRORTYPE err = GetParameter(OMX_IndexParamPortDefinition,
|
||||
&def,
|
||||
sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
|
||||
if (err != OMX_ErrorNone) {
|
||||
return NS_ERROR_FAILURE;
|
||||
} else if (def.eDir == aType) {
|
||||
LOG("Get OMX_IndexParamPortDefinition: port: %d, type: %d", def.nPortIndex, def.eDir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t t = 0;
|
||||
|
||||
// Configure video output GraphicBuffer for video decoding acceleration.
|
||||
bool useGralloc = false;
|
||||
if (aType == OMX_DirOutput && mQuirks.test(kRequiresAllocateBufferOnOutputPorts) &&
|
||||
(def.eDomain == OMX_PortDomainVideo)) {
|
||||
if (NS_FAILED(EnableOmxGraphicBufferPort(def))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
LOG("Enable OMX GraphicBuffer port, number %d, width %d, height %d", def.nBufferCountActual,
|
||||
def.format.video.nFrameWidth, def.format.video.nFrameHeight);
|
||||
|
||||
useGralloc = true;
|
||||
|
||||
t = 1024; // MemoryDealer doesn't like 0, it's just for MemoryDealer happy.
|
||||
} else {
|
||||
t = def.nBufferCountActual * def.nBufferSize;
|
||||
LOG("Buffer count %d, buffer size %d", def.nBufferCountActual, def.nBufferSize);
|
||||
}
|
||||
|
||||
bool liveinlocal = mOmx->livesLocally(mNode, getpid());
|
||||
|
||||
// MemoryDealer is a IPC buffer allocator in Gonk because IOMX is actually
|
||||
// lives in mediaserver.
|
||||
mMemoryDealer[aType] = new MemoryDealer(t, "Gecko-OMX");
|
||||
for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
|
||||
RefPtr<GonkBufferData> buffer;
|
||||
IOMX::buffer_id bufferID;
|
||||
status_t st;
|
||||
nsresult rv;
|
||||
|
||||
buffer = new GonkBufferData(liveinlocal, this);
|
||||
if (useGralloc) {
|
||||
// Buffer is lived remotely. Use GraphicBuffer for decoded video frame display.
|
||||
rv = buffer->InitGraphicBuffer(def.format.video);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
st = mOmx->useGraphicBuffer(mNode,
|
||||
def.nPortIndex,
|
||||
buffer->mTextureClientRecycleHandler->GetGraphicBuffer(),
|
||||
&bufferID);
|
||||
CHECK_ERR(st);
|
||||
} else {
|
||||
sp<IMemory> mem = mMemoryDealer[aType]->allocate(def.nBufferSize);
|
||||
MOZ_ASSERT(mem.get());
|
||||
|
||||
if ((mQuirks.test(kRequiresAllocateBufferOnInputPorts) && aType == OMX_DirInput) ||
|
||||
(mQuirks.test(kRequiresAllocateBufferOnOutputPorts) && aType == OMX_DirOutput)) {
|
||||
// Buffer is lived remotely. We allocate a local OMX_BUFFERHEADERTYPE
|
||||
// as the mirror of the remote OMX_BUFFERHEADERTYPE.
|
||||
st = mOmx->allocateBufferWithBackup(mNode, aType, mem, &bufferID);
|
||||
CHECK_ERR(st);
|
||||
rv = buffer->InitSharedMemory(mem.get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
// Buffer is lived locally, bufferID is the actually OMX_BUFFERHEADERTYPE
|
||||
// pointer.
|
||||
st = mOmx->useBuffer(mNode, aType, mem, &bufferID);
|
||||
CHECK_ERR(st);
|
||||
rv = buffer->InitLocalBuffer(bufferID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
rv = buffer->SetBufferId(bufferID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aBufferList->AppendElement(buffer);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkOmxPlatformLayer::ReleaseOmxBuffer(OMX_DIRTYPE aType,
|
||||
BUFFERLIST* aBufferList)
|
||||
{
|
||||
status_t st;
|
||||
uint32_t len = aBufferList->Length();
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
GonkBufferData* buffer = static_cast<GonkBufferData*>(aBufferList->ElementAt(i).get());
|
||||
IOMX::buffer_id id = (OMX_BUFFERHEADERTYPE*) buffer->ID();
|
||||
st = mOmx->freeBuffer(mNode, aType, id);
|
||||
if (st != OK) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
buffer->ReleaseBuffer();
|
||||
}
|
||||
aBufferList->Clear();
|
||||
mMemoryDealer[aType].clear();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkOmxPlatformLayer::EnableOmxGraphicBufferPort(OMX_PARAM_PORTDEFINITIONTYPE& aDef)
|
||||
{
|
||||
status_t st;
|
||||
|
||||
st = mOmx->enableGraphicBuffers(mNode, aDef.nPortIndex, OMX_TRUE);
|
||||
CHECK_ERR(st);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
OMX_ERRORTYPE
|
||||
GonkOmxPlatformLayer::GetState(OMX_STATETYPE* aType)
|
||||
{
|
||||
return (OMX_ERRORTYPE)mOmx->getState(mNode, aType);
|
||||
}
|
||||
|
||||
OMX_ERRORTYPE
|
||||
GonkOmxPlatformLayer::GetParameter(OMX_INDEXTYPE aParamIndex,
|
||||
OMX_PTR aComponentParameterStructure,
|
||||
OMX_U32 aComponentParameterSize)
|
||||
{
|
||||
return (OMX_ERRORTYPE)mOmx->getParameter(mNode,
|
||||
aParamIndex,
|
||||
aComponentParameterStructure,
|
||||
aComponentParameterSize);
|
||||
}
|
||||
|
||||
OMX_ERRORTYPE
|
||||
GonkOmxPlatformLayer::SetParameter(OMX_INDEXTYPE aParamIndex,
|
||||
OMX_PTR aComponentParameterStructure,
|
||||
OMX_U32 aComponentParameterSize)
|
||||
{
|
||||
return (OMX_ERRORTYPE)mOmx->setParameter(mNode,
|
||||
aParamIndex,
|
||||
aComponentParameterStructure,
|
||||
aComponentParameterSize);
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkOmxPlatformLayer::Shutdown()
|
||||
{
|
||||
mOmx->freeNode(mNode);
|
||||
mOmxObserver->Shutdown();
|
||||
mOmxObserver = nullptr;
|
||||
mOmxClient.disconnect();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
OMX_ERRORTYPE
|
||||
GonkOmxPlatformLayer::InitOmxToStateLoaded(const TrackInfo* aInfo)
|
||||
{
|
||||
mInfo = aInfo;
|
||||
status_t err = mOmxClient.connect();
|
||||
if (err != OK) {
|
||||
return OMX_ErrorUndefined;
|
||||
}
|
||||
mOmx = mOmxClient.interface();
|
||||
if (!mOmx.get()) {
|
||||
return OMX_ErrorUndefined;
|
||||
}
|
||||
|
||||
LOG("find componenet for mime type %s", mInfo->mMimeType.Data());
|
||||
|
||||
nsTArray<ComponentInfo> components;
|
||||
if (FindComponents(mInfo->mMimeType, &components)) {
|
||||
for (auto comp : components) {
|
||||
if (LoadComponent(comp)) {
|
||||
return OMX_ErrorNone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG("no component is loaded");
|
||||
return OMX_ErrorUndefined;
|
||||
}
|
||||
|
||||
OMX_ERRORTYPE
|
||||
GonkOmxPlatformLayer::EmptyThisBuffer(BufferData* aData)
|
||||
{
|
||||
return (OMX_ERRORTYPE)mOmx->emptyBuffer(mNode,
|
||||
(IOMX::buffer_id)aData->ID(),
|
||||
aData->mBuffer->nOffset,
|
||||
aData->mBuffer->nFilledLen,
|
||||
aData->mBuffer->nFlags,
|
||||
aData->mBuffer->nTimeStamp);
|
||||
}
|
||||
|
||||
OMX_ERRORTYPE
|
||||
GonkOmxPlatformLayer::FillThisBuffer(BufferData* aData)
|
||||
{
|
||||
return (OMX_ERRORTYPE)mOmx->fillBuffer(mNode, (IOMX::buffer_id)aData->ID());
|
||||
}
|
||||
|
||||
OMX_ERRORTYPE
|
||||
GonkOmxPlatformLayer::SendCommand(OMX_COMMANDTYPE aCmd,
|
||||
OMX_U32 aParam1,
|
||||
OMX_PTR aCmdData)
|
||||
{
|
||||
return (OMX_ERRORTYPE)mOmx->sendCommand(mNode, aCmd, aParam1);
|
||||
}
|
||||
|
||||
bool
|
||||
GonkOmxPlatformLayer::LoadComponent(const ComponentInfo& aComponent)
|
||||
{
|
||||
status_t err = mOmx->allocateNode(aComponent.mName, mOmxObserver, &mNode);
|
||||
if (err == OK) {
|
||||
mQuirks = aComponent.mQuirks;
|
||||
LOG("Load OpenMax component %s, alloc input %d, alloc output %d, live locally %d",
|
||||
aComponent.mName, mQuirks.test(kRequiresAllocateBufferOnInputPorts),
|
||||
mQuirks.test(kRequiresAllocateBufferOnOutputPorts),
|
||||
mOmx->livesLocally(mNode, getpid()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
layers::ImageContainer*
|
||||
GonkOmxPlatformLayer::GetImageContainer()
|
||||
{
|
||||
return mImageContainer;
|
||||
}
|
||||
|
||||
const TrackInfo*
|
||||
GonkOmxPlatformLayer::GetTrackInfo()
|
||||
{
|
||||
return mInfo;
|
||||
}
|
||||
|
||||
bool
|
||||
GonkOmxPlatformLayer::FindComponents(const nsACString& aMimeType,
|
||||
nsTArray<ComponentInfo>* aComponents)
|
||||
{
|
||||
static const MediaCodecList* codecs = MediaCodecList::getInstance();
|
||||
|
||||
bool useHardwareCodecOnly = false;
|
||||
|
||||
// H264 and H263 has different profiles, software codec doesn't support high profile.
|
||||
// So we use hardware codec only.
|
||||
if (!IsInEmulator() &&
|
||||
(aMimeType.EqualsLiteral("video/avc") ||
|
||||
aMimeType.EqualsLiteral("video/mp4") ||
|
||||
aMimeType.EqualsLiteral("video/mp4v-es") ||
|
||||
aMimeType.EqualsLiteral("video/3gp"))) {
|
||||
useHardwareCodecOnly = true;
|
||||
}
|
||||
|
||||
const char* mime = aMimeType.Data();
|
||||
// Translate VP8 MIME type to Android format.
|
||||
if (aMimeType.EqualsLiteral("video/webm; codecs=vp8")) {
|
||||
mime = "video/x-vnd.on2.vp8";
|
||||
}
|
||||
|
||||
size_t start = 0;
|
||||
bool found = false;
|
||||
while (true) {
|
||||
ssize_t index = codecs->findCodecByType(mime, false /* encoder */, start);
|
||||
if (index < 0) {
|
||||
break;
|
||||
}
|
||||
start = index + 1;
|
||||
|
||||
const char* name = codecs->getCodecName(index);
|
||||
if (IsSoftwareCodec(name) && useHardwareCodecOnly) {
|
||||
continue;
|
||||
}
|
||||
|
||||
found = true;
|
||||
|
||||
if (!aComponents) {
|
||||
continue;
|
||||
}
|
||||
ComponentInfo* comp = aComponents->AppendElement();
|
||||
comp->mName = name;
|
||||
if (codecs->codecHasQuirk(index, "requires-allocate-on-input-ports")) {
|
||||
comp->mQuirks.set(kRequiresAllocateBufferOnInputPorts);
|
||||
}
|
||||
if (codecs->codecHasQuirk(index, "requires-allocate-on-output-ports")) {
|
||||
comp->mQuirks.set(kRequiresAllocateBufferOnOutputPorts);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
OMX_VIDEO_CODINGTYPE
|
||||
GonkOmxPlatformLayer::CompressionFormat()
|
||||
{
|
||||
MOZ_ASSERT(mInfo);
|
||||
|
||||
return mInfo->mMimeType.EqualsLiteral("video/webm; codecs=vp8") ?
|
||||
ANDROID_OMX_VIDEO_CodingVP8 : OmxPlatformLayer::CompressionFormat();
|
||||
}
|
||||
|
||||
} // mozilla
|
||||
205
dom/media/platforms/omx/GonkOmxPlatformLayer.h
Normal file
205
dom/media/platforms/omx/GonkOmxPlatformLayer.h
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
/* -*- 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(GonkOmxPlatformLayer_h_)
|
||||
#define GonkOmxPlatformLayer_h_
|
||||
|
||||
#pragma GCC visibility push(default)
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include <utils/RefBase.h>
|
||||
#include <media/stagefright/OMXClient.h>
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
#include "OMX_Component.h"
|
||||
|
||||
#include "OmxPlatformLayer.h"
|
||||
|
||||
class nsACString;
|
||||
|
||||
namespace android {
|
||||
class IMemory;
|
||||
class MemoryDealer;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class GonkOmxObserver;
|
||||
class GonkOmxPlatformLayer;
|
||||
class GonkTextureClientRecycleHandler;
|
||||
|
||||
/*
|
||||
* Due to Android's omx node could live in local process (client) or remote
|
||||
* process (mediaserver). And there are 3 kinds of buffer in Android OMX.
|
||||
*
|
||||
* 1.
|
||||
* When buffer is in local process, the IOMX::buffer_id is OMX_BUFFERHEADERTYPE
|
||||
* pointer actually, it is safe to use it directly.
|
||||
*
|
||||
* 2.
|
||||
* When buffer is in remote process, the OMX_BUFFERHEADERTYPE pointer is 'IN' the
|
||||
* remote process. It can't be used in local process, so here it allocates a
|
||||
* local OMX_BUFFERHEADERTYPE. The raw/decoded data is in the android shared
|
||||
* memory, IMemory.
|
||||
*
|
||||
* 3.
|
||||
* When buffer is in remote process for the display output port. It uses
|
||||
* GraphicBuffer to accelerate the decoding and display.
|
||||
*
|
||||
*/
|
||||
class GonkBufferData : public OmxPromiseLayer::BufferData {
|
||||
protected:
|
||||
virtual ~GonkBufferData() {}
|
||||
|
||||
public:
|
||||
GonkBufferData(bool aLiveInLocal,
|
||||
GonkOmxPlatformLayer* aLayer);
|
||||
|
||||
BufferID ID() override
|
||||
{
|
||||
return mId;
|
||||
}
|
||||
|
||||
already_AddRefed<MediaData> GetPlatformMediaData() override;
|
||||
|
||||
bool IsLocalBuffer()
|
||||
{
|
||||
return !!mMirrorBuffer.get();
|
||||
}
|
||||
|
||||
void ReleaseBuffer();
|
||||
|
||||
nsresult SetBufferId(android::IOMX::buffer_id aId)
|
||||
{
|
||||
mId = aId;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// The mBuffer is in local process. And aId is actually the OMX_BUFFERHEADERTYPE
|
||||
// pointer. It doesn't need a mirror buffer.
|
||||
nsresult InitLocalBuffer(android::IOMX::buffer_id aId);
|
||||
|
||||
// aMemory is an IPC based memory which will be used as the pBuffer in
|
||||
// mBuffer. And the mBuffer will be the mirror OMX_BUFFERHEADERTYPE
|
||||
// of the one in the remote process.
|
||||
nsresult InitSharedMemory(android::IMemory* aMemory);
|
||||
|
||||
// GraphicBuffer is for video decoding acceleration on output port.
|
||||
// Then mBuffer is the mirror OMX_BUFFERHEADERTYPE of the one in the remote
|
||||
// process.
|
||||
nsresult InitGraphicBuffer(OMX_VIDEO_PORTDEFINITIONTYPE& aDef);
|
||||
|
||||
// Android OMX uses this id to pass the buffer between OMX component and
|
||||
// client.
|
||||
android::IOMX::buffer_id mId;
|
||||
|
||||
// mMirrorBuffer are used only when the omx node is in mediaserver.
|
||||
// Due to IPC problem, the mId is the OMX_BUFFERHEADERTYPE address in mediaserver.
|
||||
// It can't mapping to client process, so we need a local OMX_BUFFERHEADERTYPE
|
||||
// here to mirror the remote OMX_BUFFERHEADERTYPE in mediaserver.
|
||||
nsAutoPtr<OMX_BUFFERHEADERTYPE> mMirrorBuffer;
|
||||
|
||||
// It creates GraphicBuffer and manages TextureClient.
|
||||
RefPtr<GonkTextureClientRecycleHandler> mTextureClientRecycleHandler;
|
||||
|
||||
GonkOmxPlatformLayer* mGonkPlatformLayer;
|
||||
};
|
||||
|
||||
class GonkOmxPlatformLayer : public OmxPlatformLayer {
|
||||
public:
|
||||
enum {
|
||||
kRequiresAllocateBufferOnInputPorts = 0,
|
||||
kRequiresAllocateBufferOnOutputPorts,
|
||||
QUIRKS,
|
||||
};
|
||||
typedef std::bitset<QUIRKS> Quirks;
|
||||
|
||||
struct ComponentInfo {
|
||||
const char* mName;
|
||||
Quirks mQuirks;
|
||||
};
|
||||
|
||||
GonkOmxPlatformLayer(OmxDataDecoder* aDataDecoder,
|
||||
OmxPromiseLayer* aPromiseLayer,
|
||||
TaskQueue* aTaskQueue,
|
||||
layers::ImageContainer* aImageContainer);
|
||||
|
||||
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 nIndex,
|
||||
OMX_PTR aComponentParameterStructure,
|
||||
OMX_U32 aComponentParameterSize) override;
|
||||
|
||||
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 Shutdown() override;
|
||||
|
||||
static bool FindComponents(const nsACString& aMimeType,
|
||||
nsTArray<ComponentInfo>* aComponents = nullptr);
|
||||
|
||||
// Android/QCOM decoder uses its own OMX_VIDEO_CodingVP8 definition in
|
||||
// frameworks/native/media/include/openmax/OMX_Video.h, not the one defined
|
||||
// in OpenMAX v1.1.2 OMX_VideoExt.h
|
||||
OMX_VIDEO_CODINGTYPE CompressionFormat() override;
|
||||
|
||||
protected:
|
||||
friend GonkBufferData;
|
||||
|
||||
layers::ImageContainer* GetImageContainer();
|
||||
|
||||
const TrackInfo* GetTrackInfo();
|
||||
|
||||
TaskQueue* GetTaskQueue()
|
||||
{
|
||||
return mTaskQueue;
|
||||
}
|
||||
|
||||
nsresult EnableOmxGraphicBufferPort(OMX_PARAM_PORTDEFINITIONTYPE& aDef);
|
||||
|
||||
bool LoadComponent(const ComponentInfo& aComponent);
|
||||
|
||||
friend class GonkOmxObserver;
|
||||
|
||||
RefPtr<TaskQueue> mTaskQueue;
|
||||
|
||||
RefPtr<layers::ImageContainer> mImageContainer;
|
||||
|
||||
// OMX_DirInput is 0, OMX_DirOutput is 1.
|
||||
android::sp<android::MemoryDealer> mMemoryDealer[2];
|
||||
|
||||
android::sp<GonkOmxObserver> mOmxObserver;
|
||||
|
||||
android::sp<android::IOMX> mOmx;
|
||||
|
||||
android::IOMX::node_id mNode;
|
||||
|
||||
android::OMXClient mOmxClient;
|
||||
|
||||
Quirks mQuirks;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#pragma GCC visibility pop
|
||||
|
||||
#endif // GonkOmxPlatformLayer_h_
|
||||
1038
dom/media/platforms/omx/OmxDataDecoder.cpp
Normal file
1038
dom/media/platforms/omx/OmxDataDecoder.cpp
Normal file
File diff suppressed because it is too large
Load diff
214
dom/media/platforms/omx/OmxDataDecoder.h
Normal file
214
dom/media/platforms/omx/OmxDataDecoder.h
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
/* -*- 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 "mozilla/Monitor.h"
|
||||
|
||||
#include "AudioCompactor.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "MediaInfo.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
|
||||
#include "OMX_Component.h"
|
||||
|
||||
#include "OmxPromiseLayer.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;
|
||||
|
||||
/* 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 : public MediaDataDecoder {
|
||||
protected:
|
||||
virtual ~OmxDataDecoder();
|
||||
|
||||
public:
|
||||
OmxDataDecoder(const TrackInfo& aTrackInfo,
|
||||
MediaDataDecoderCallback* aCallback,
|
||||
layers::ImageContainer* aImageContainer);
|
||||
|
||||
RefPtr<InitPromise> Init() override;
|
||||
|
||||
void Input(MediaRawData* aSample) override;
|
||||
|
||||
void Flush() override;
|
||||
|
||||
void Drain() override;
|
||||
|
||||
void Shutdown() override;
|
||||
|
||||
const char* GetDescriptionName() const override
|
||||
{
|
||||
return "omx decoder";
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
void DoAsyncShutdown();
|
||||
|
||||
void 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);
|
||||
|
||||
Monitor mMonitor;
|
||||
|
||||
// The Omx TaskQueue.
|
||||
RefPtr<TaskQueue> mOmxTaskQueue;
|
||||
|
||||
RefPtr<TaskQueue> mReaderTaskQueue;
|
||||
|
||||
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 TaskQeueu.
|
||||
Atomic<bool> mShuttingDown;
|
||||
|
||||
// It is accessed in Omx TaskQeueu.
|
||||
bool mCheckingInputExhausted;
|
||||
|
||||
// It is accessed in reader TaskQueue.
|
||||
MozPromiseHolder<InitPromise> mInitPromise;
|
||||
|
||||
// It is written in Omx TaskQeueu. 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;
|
||||
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
void InitOmxParameter(T* aParam)
|
||||
{
|
||||
PodZero(aParam);
|
||||
aParam->nSize = sizeof(T);
|
||||
aParam->nVersion.s.nVersionMajor = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* OmxDataDecoder_h_ */
|
||||
45
dom/media/platforms/omx/OmxDecoderModule.cpp
Normal file
45
dom/media/platforms/omx/OmxDecoderModule.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/* -*- 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"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
OmxDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
|
||||
{
|
||||
RefPtr<OmxDataDecoder> decoder = new OmxDataDecoder(aParams.mConfig,
|
||||
aParams.mCallback,
|
||||
aParams.mImageContainer);
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
OmxDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams)
|
||||
{
|
||||
RefPtr<OmxDataDecoder> decoder = new OmxDataDecoder(aParams.mConfig,
|
||||
aParams.mCallback,
|
||||
nullptr);
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
PlatformDecoderModule::ConversionRequired
|
||||
OmxDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
|
||||
{
|
||||
return ConversionRequired::kNeedNone;
|
||||
}
|
||||
|
||||
bool
|
||||
OmxDecoderModule::SupportsMimeType(const nsACString& aMimeType,
|
||||
DecoderDoctorDiagnostics* aDiagnostics) const
|
||||
{
|
||||
return OmxPlatformLayer::SupportsMimeType(aMimeType);
|
||||
}
|
||||
|
||||
}
|
||||
30
dom/media/platforms/omx/OmxDecoderModule.h
Normal file
30
dom/media/platforms/omx/OmxDecoderModule.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/* -*- 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:
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
CreateVideoDecoder(const CreateDecoderParams& aParams) override;
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
CreateAudioDecoder(const CreateDecoderParams& aParams) override;
|
||||
|
||||
bool SupportsMimeType(const nsACString& aMimeType,
|
||||
DecoderDoctorDiagnostics* aDiagnostics) const override;
|
||||
|
||||
ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const override;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // OmxDecoderModule_h_
|
||||
327
dom/media/platforms/omx/OmxPlatformLayer.cpp
Normal file
327
dom/media/platforms/omx/OmxPlatformLayer.cpp
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
/* -*- 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 "OMX_VideoExt.h" // For VP8.
|
||||
|
||||
#if defined(MOZ_WIDGET_GONK) && (ANDROID_VERSION == 20 || ANDROID_VERSION == 19)
|
||||
#define OMX_PLATFORM_GONK
|
||||
#include "GonkOmxPlatformLayer.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() {}
|
||||
// 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_ERRORTYPE err;
|
||||
|
||||
OMX_PARAM_PORTDEFINITIONTYPE def;
|
||||
InitOmxParameter(&def);
|
||||
def.nPortIndex = aOmx.OutputPortIndex();
|
||||
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 %d, sample rate %d",
|
||||
pcmParams.nChannels, pcmParams.nSamplingRate);
|
||||
|
||||
return OMX_ErrorNone;
|
||||
}
|
||||
|
||||
class OmxAacConfig : public OmxAudioConfig
|
||||
{
|
||||
public:
|
||||
OMX_ERRORTYPE Apply(OmxPlatformLayer& aOmx, const AudioInfo& aInfo) override
|
||||
{
|
||||
OMX_ERRORTYPE err;
|
||||
|
||||
OMX_AUDIO_PARAM_AACPROFILETYPE aacProfile;
|
||||
InitOmxParameter(&aacProfile);
|
||||
aacProfile.nPortIndex = aOmx.InputPortIndex();
|
||||
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 %d, sample rate %d, 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_ERRORTYPE err;
|
||||
|
||||
OMX_AUDIO_PARAM_MP3TYPE mp3Param;
|
||||
InitOmxParameter(&mp3Param);
|
||||
mp3Param.nPortIndex = aOmx.InputPortIndex();
|
||||
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 %d, sample rate %d",
|
||||
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_ERRORTYPE err;
|
||||
|
||||
OMX_AUDIO_PARAM_AMRTYPE def;
|
||||
InitOmxParameter(&def);
|
||||
def.nPortIndex = aOmx.InputPortIndex();
|
||||
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 Move(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_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 %d", 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 Move(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_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_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.
|
||||
#ifdef OMX_PLATFORM_GONK
|
||||
|
||||
bool
|
||||
OmxPlatformLayer::SupportsMimeType(const nsACString& aMimeType)
|
||||
{
|
||||
return GonkOmxPlatformLayer::FindComponents(aMimeType);
|
||||
}
|
||||
|
||||
OmxPlatformLayer*
|
||||
OmxPlatformLayer::Create(OmxDataDecoder* aDataDecoder,
|
||||
OmxPromiseLayer* aPromiseLayer,
|
||||
TaskQueue* aTaskQueue,
|
||||
layers::ImageContainer* aImageContainer)
|
||||
{
|
||||
return new GonkOmxPlatformLayer(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
|
||||
|
||||
}
|
||||
100
dom/media/platforms/omx/OmxPlatformLayer.h
Normal file
100
dom/media/platforms/omx/OmxPlatformLayer.h
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/* -*- 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 "OmxPromiseLayer.h"
|
||||
|
||||
class nsACString;
|
||||
|
||||
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; for example, video decoding needs gralloc
|
||||
// on Gonk. 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() {}
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OmxPlatformLayer_h_
|
||||
382
dom/media/platforms/omx/OmxPromiseLayer.cpp
Normal file
382
dom/media/platforms/omx/OmxPromiseLayer.cpp
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
/* -*- 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 = 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(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 %d", 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(Move(failure), __func__);
|
||||
} else {
|
||||
if (aData->mRawData) {
|
||||
mRawDatas.AppendElement(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 == 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 %d", 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();
|
||||
}
|
||||
|
||||
}
|
||||
252
dom/media/platforms/omx/OmxPromiseLayer.h
Normal file
252
dom/media/platforms/omx/OmxPromiseLayer.h
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
/* -*- 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 "nsAutoPtr.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() {}
|
||||
|
||||
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() {}
|
||||
|
||||
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, in platform like gonk, it is 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;
|
||||
|
||||
nsAutoPtr<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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* OmxPromiseLayer_h_ */
|
||||
57
dom/media/platforms/omx/moz.build
Normal file
57
dom/media/platforms/omx/moz.build
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# -*- 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_WIDGET_TOOLKIT'] == 'gonk' and (CONFIG['ANDROID_VERSION'] == '19' or CONFIG['ANDROID_VERSION'] == '20'):
|
||||
# Suppress some GCC/clang warnings being treated as errors:
|
||||
# - about attributes on forward declarations for types that are already
|
||||
# defined, which complains about an important MOZ_EXPORT for android::AString
|
||||
# - about multi-character constants which are used in codec-related code
|
||||
if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']:
|
||||
CXXFLAGS += [
|
||||
'-Wno-error=attributes',
|
||||
'-Wno-error=multichar'
|
||||
]
|
||||
CXXFLAGS += [
|
||||
'-I%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
|
||||
'frameworks/base/include/binder',
|
||||
'frameworks/base/include/utils',
|
||||
]
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'GonkOmxPlatformLayer.cpp',
|
||||
]
|
||||
EXTRA_DSO_LDOPTS += [
|
||||
'-libbinder',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-error=shadow']
|
||||
|
||||
if CONFIG['_MSC_VER']:
|
||||
# Avoid warnings from third-party code that we can not modify.
|
||||
if CONFIG['CLANG_CL']:
|
||||
CXXFLAGS += ['-Wno-invalid-source-encoding']
|
||||
else:
|
||||
CXXFLAGS += ['-validate-charset-']
|
||||
Loading…
Add table
Add a link
Reference in a new issue