mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-08 00:07:30 +09:00
667 lines
20 KiB
C++
667 lines
20 KiB
C++
/* -*- 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
|