summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/media/IOMX.h7
-rw-r--r--include/media/stagefright/ACodec.h6
-rw-r--r--include/media/stagefright/BufferProducerWrapper.h46
-rw-r--r--include/media/stagefright/MediaCodec.h6
-rw-r--r--media/libmedia/IOMX.cpp113
-rw-r--r--media/libstagefright/ACodec.cpp72
-rw-r--r--media/libstagefright/MediaCodec.cpp101
-rw-r--r--media/libstagefright/OMXClient.cpp18
-rw-r--r--media/libstagefright/include/OMX.h6
-rw-r--r--media/libstagefright/include/OMXNodeInstance.h24
-rw-r--r--media/libstagefright/omx/Android.mk2
-rw-r--r--media/libstagefright/omx/GraphicBufferSource.cpp441
-rw-r--r--media/libstagefright/omx/GraphicBufferSource.h176
-rw-r--r--media/libstagefright/omx/OMX.cpp14
-rw-r--r--media/libstagefright/omx/OMXNodeInstance.cpp136
15 files changed, 1148 insertions, 20 deletions
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index be1b2fc..0b1d1e4 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -19,6 +19,7 @@
#define ANDROID_IOMX_H_
#include <binder/IInterface.h>
+#include <gui/IGraphicBufferProducer.h>
#include <ui/GraphicBuffer.h>
#include <utils/List.h>
#include <utils/String8.h>
@@ -96,6 +97,12 @@ public:
node_id node, OMX_U32 port_index,
const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) = 0;
+ virtual status_t createInputSurface(
+ node_id node, OMX_U32 port_index,
+ sp<IGraphicBufferProducer> *bufferProducer) = 0;
+
+ virtual status_t signalEndOfInputStream(node_id node) = 0;
+
// This API clearly only makes sense if the caller lives in the
// same process as the callee, i.e. is the media_server, as the
// returned "buffer_data" pointer is just that, a pointer into local
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 317b6f0..097ec5f 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -43,6 +43,8 @@ struct ACodec : public AHierarchicalStateMachine {
kWhatError = 'erro',
kWhatComponentAllocated = 'cAll',
kWhatComponentConfigured = 'cCon',
+ kWhatInputSurfaceCreated = 'isfc',
+ kWhatSignaledInputEOS = 'seos',
kWhatBuffersAllocated = 'allc',
};
@@ -55,9 +57,11 @@ struct ACodec : public AHierarchicalStateMachine {
void initiateShutdown(bool keepComponentAllocated = false);
void signalSetParameters(const sp<AMessage> &msg);
+ void signalEndOfInputStream();
void initiateAllocateComponent(const sp<AMessage> &msg);
void initiateConfigureComponent(const sp<AMessage> &msg);
+ void initiateCreateInputSurface();
void initiateStart();
void signalRequestIDRFrame();
@@ -105,6 +109,8 @@ private:
kWhatDrainDeferredMessages = 'drai',
kWhatAllocateComponent = 'allo',
kWhatConfigureComponent = 'conf',
+ kWhatCreateInputSurface = 'cisf',
+ kWhatSignalEndOfInputStream = 'eois',
kWhatStart = 'star',
kWhatRequestIDRFrame = 'ridr',
kWhatSetParameters = 'setP',
diff --git a/include/media/stagefright/BufferProducerWrapper.h b/include/media/stagefright/BufferProducerWrapper.h
new file mode 100644
index 0000000..d8acf30
--- /dev/null
+++ b/include/media/stagefright/BufferProducerWrapper.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BUFFER_PRODUCER_WRAPPER_H_
+
+#define BUFFER_PRODUCER_WRAPPER_H_
+
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+// Can't use static_cast to cast a RefBase back to an IGraphicBufferProducer,
+// because IGBP's parent (IInterface) uses virtual inheritance. This class
+// wraps IGBP while we pass it through AMessage.
+
+struct BufferProducerWrapper : RefBase {
+ BufferProducerWrapper(
+ const sp<IGraphicBufferProducer>& bufferProducer) :
+ mBufferProducer(bufferProducer) { }
+
+ sp<IGraphicBufferProducer> getBufferProducer() const {
+ return mBufferProducer;
+ }
+
+private:
+ const sp<IGraphicBufferProducer> mBufferProducer;
+
+ DISALLOW_EVIL_CONSTRUCTORS(BufferProducerWrapper);
+};
+
+} // namespace android
+
+#endif // BUFFER_PRODUCER_WRAPPER_H_
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 1002663..ef695a7 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -56,6 +56,8 @@ struct MediaCodec : public AHandler {
const sp<ICrypto> &crypto,
uint32_t flags);
+ status_t createInputSurface(sp<IGraphicBufferProducer>* bufferProducer);
+
status_t start();
// Returns to a state in which the component remains allocated but
@@ -101,6 +103,8 @@ struct MediaCodec : public AHandler {
status_t renderOutputBufferAndRelease(size_t index);
status_t releaseOutputBuffer(size_t index);
+ status_t signalEndOfInputStream();
+
status_t getOutputFormat(sp<AMessage> *format) const;
status_t getInputBuffers(Vector<sp<ABuffer> > *buffers) const;
@@ -143,6 +147,7 @@ private:
enum {
kWhatInit = 'init',
kWhatConfigure = 'conf',
+ kWhatCreateInputSurface = 'cisf',
kWhatStart = 'strt',
kWhatStop = 'stop',
kWhatRelease = 'rele',
@@ -150,6 +155,7 @@ private:
kWhatQueueInputBuffer = 'queI',
kWhatDequeueOutputBuffer = 'deqO',
kWhatReleaseOutputBuffer = 'relO',
+ kWhatSignalEndOfInputStream = 'eois',
kWhatGetBuffers = 'getB',
kWhatFlush = 'flus',
kWhatGetOutputFormat = 'getO',
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index 48e427a..d6cd43a 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -40,6 +40,8 @@ enum {
ENABLE_GRAPHIC_BUFFERS,
USE_BUFFER,
USE_GRAPHIC_BUFFER,
+ CREATE_INPUT_SURFACE,
+ SIGNAL_END_OF_INPUT_STREAM,
STORE_META_DATA_IN_BUFFERS,
ALLOC_BUFFER,
ALLOC_BUFFER_WITH_BACKUP,
@@ -280,6 +282,45 @@ public:
return err;
}
+ virtual status_t createInputSurface(
+ node_id node, OMX_U32 port_index,
+ sp<IGraphicBufferProducer> *bufferProducer) {
+ Parcel data, reply;
+ status_t err;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ data.writeInt32(port_index);
+ err = remote()->transact(CREATE_INPUT_SURFACE, data, &reply);
+ if (err != OK) {
+ ALOGW("binder transaction failed: %d", err);
+ return err;
+ }
+
+ err = reply.readInt32();
+ if (err != OK) {
+ return err;
+ }
+
+ *bufferProducer = IGraphicBufferProducer::asInterface(
+ reply.readStrongBinder());
+
+ return err;
+ }
+
+ virtual status_t signalEndOfInputStream(node_id node) {
+ Parcel data, reply;
+ status_t err;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ err = remote()->transact(SIGNAL_END_OF_INPUT_STREAM, data, &reply);
+ if (err != OK) {
+ ALOGW("binder transaction failed: %d", err);
+ return err;
+ }
+
+ return reply.readInt32();
+ }
+
virtual status_t storeMetaDataInBuffers(
node_id node, OMX_U32 port_index, OMX_BOOL enable) {
Parcel data, reply;
@@ -404,7 +445,7 @@ IMPLEMENT_META_INTERFACE(OMX, "android.hardware.IOMX");
////////////////////////////////////////////////////////////////////////////////
-#define CHECK_INTERFACE(interface, data, reply) \
+#define CHECK_OMX_INTERFACE(interface, data, reply) \
do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
ALOGW("Call incorrectly routed to " #interface); \
return PERMISSION_DENIED; \
@@ -415,7 +456,7 @@ status_t BnOMX::onTransact(
switch (code) {
case LIVES_LOCALLY:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
node_id node = (void *)data.readIntPtr();
pid_t pid = (pid_t)data.readInt32();
reply->writeInt32(livesLocally(node, pid));
@@ -425,7 +466,7 @@ status_t BnOMX::onTransact(
case LIST_NODES:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
List<ComponentInfo> list;
listNodes(&list);
@@ -448,7 +489,7 @@ status_t BnOMX::onTransact(
case ALLOCATE_NODE:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
const char *name = data.readCString();
@@ -468,7 +509,7 @@ status_t BnOMX::onTransact(
case FREE_NODE:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
node_id node = (void*)data.readIntPtr();
@@ -479,7 +520,7 @@ status_t BnOMX::onTransact(
case SEND_COMMAND:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
node_id node = (void*)data.readIntPtr();
@@ -497,7 +538,7 @@ status_t BnOMX::onTransact(
case GET_CONFIG:
case SET_CONFIG:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
node_id node = (void*)data.readIntPtr();
OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
@@ -539,7 +580,7 @@ status_t BnOMX::onTransact(
case GET_STATE:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
node_id node = (void*)data.readIntPtr();
OMX_STATETYPE state = OMX_StateInvalid;
@@ -553,7 +594,7 @@ status_t BnOMX::onTransact(
case ENABLE_GRAPHIC_BUFFERS:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
node_id node = (void*)data.readIntPtr();
OMX_U32 port_index = data.readInt32();
@@ -567,7 +608,7 @@ status_t BnOMX::onTransact(
case GET_GRAPHIC_BUFFER_USAGE:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
node_id node = (void*)data.readIntPtr();
OMX_U32 port_index = data.readInt32();
@@ -582,7 +623,7 @@ status_t BnOMX::onTransact(
case USE_BUFFER:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
node_id node = (void*)data.readIntPtr();
OMX_U32 port_index = data.readInt32();
@@ -602,7 +643,7 @@ status_t BnOMX::onTransact(
case USE_GRAPHIC_BUFFER:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
node_id node = (void*)data.readIntPtr();
OMX_U32 port_index = data.readInt32();
@@ -621,9 +662,41 @@ status_t BnOMX::onTransact(
return NO_ERROR;
}
+ case CREATE_INPUT_SURFACE:
+ {
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+ OMX_U32 port_index = data.readInt32();
+
+ sp<IGraphicBufferProducer> bufferProducer;
+ status_t err = createInputSurface(node, port_index,
+ &bufferProducer);
+
+ reply->writeInt32(err);
+
+ if (err == OK) {
+ reply->writeStrongBinder(bufferProducer->asBinder());
+ }
+
+ return NO_ERROR;
+ }
+
+ case SIGNAL_END_OF_INPUT_STREAM:
+ {
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+
+ status_t err = signalEndOfInputStream(node);
+ reply->writeInt32(err);
+
+ return NO_ERROR;
+ }
+
case STORE_META_DATA_IN_BUFFERS:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
node_id node = (void*)data.readIntPtr();
OMX_U32 port_index = data.readInt32();
@@ -637,7 +710,7 @@ status_t BnOMX::onTransact(
case ALLOC_BUFFER:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
node_id node = (void*)data.readIntPtr();
OMX_U32 port_index = data.readInt32();
@@ -659,7 +732,7 @@ status_t BnOMX::onTransact(
case ALLOC_BUFFER_WITH_BACKUP:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
node_id node = (void*)data.readIntPtr();
OMX_U32 port_index = data.readInt32();
@@ -681,7 +754,7 @@ status_t BnOMX::onTransact(
case FREE_BUFFER:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
node_id node = (void*)data.readIntPtr();
OMX_U32 port_index = data.readInt32();
@@ -693,7 +766,7 @@ status_t BnOMX::onTransact(
case FILL_BUFFER:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
node_id node = (void*)data.readIntPtr();
buffer_id buffer = (void*)data.readIntPtr();
@@ -704,7 +777,7 @@ status_t BnOMX::onTransact(
case EMPTY_BUFFER:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
node_id node = (void*)data.readIntPtr();
buffer_id buffer = (void*)data.readIntPtr();
@@ -723,7 +796,7 @@ status_t BnOMX::onTransact(
case GET_EXTENSION_INDEX:
{
- CHECK_INTERFACE(IOMX, data, reply);
+ CHECK_OMX_INTERFACE(IOMX, data, reply);
node_id node = (void*)data.readIntPtr();
const char *parameter_name = data.readCString();
@@ -769,7 +842,7 @@ status_t BnOMXObserver::onTransact(
switch (code) {
case OBSERVER_ON_MSG:
{
- CHECK_INTERFACE(IOMXObserver, data, reply);
+ CHECK_OMX_INTERFACE(IOMXObserver, data, reply);
omx_message msg;
data.read(&msg, sizeof(msg));
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index a6cc4eb..59fc45e 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -26,6 +26,7 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/BufferProducerWrapper.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/NativeWindowWrapper.h>
@@ -192,6 +193,7 @@ private:
friend struct ACodec::UninitializedState;
bool onConfigureComponent(const sp<AMessage> &msg);
+ void onCreateInputSurface(const sp<AMessage> &msg);
void onStart();
void onShutdown(bool keepComponentAllocated);
@@ -239,6 +241,9 @@ struct ACodec::ExecutingState : public ACodec::BaseState {
// to fill with data.
void resume();
+ // Send EOS on input stream.
+ void onSignalEndOfInputStream();
+
// Returns true iff input and output buffers are in play.
bool active() const { return mActive; }
@@ -392,6 +397,14 @@ void ACodec::initiateConfigureComponent(const sp<AMessage> &msg) {
msg->post();
}
+void ACodec::initiateCreateInputSurface() {
+ (new AMessage(kWhatCreateInputSurface, id()))->post();
+}
+
+void ACodec::signalEndOfInputStream() {
+ (new AMessage(kWhatSignalEndOfInputStream, id()))->post();
+}
+
void ACodec::initiateStart() {
(new AMessage(kWhatStart, id()))->post();
}
@@ -2469,6 +2482,14 @@ bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) {
return onOMXMessage(msg);
}
+ case ACodec::kWhatCreateInputSurface:
+ case ACodec::kWhatSignalEndOfInputStream:
+ {
+ ALOGE("Message 0x%x was not handled", msg->what());
+ mCodec->signalError(OMX_ErrorUndefined, INVALID_OPERATION);
+ return true;
+ }
+
default:
return false;
}
@@ -3232,6 +3253,13 @@ bool ACodec::LoadedState::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ case ACodec::kWhatCreateInputSurface:
+ {
+ onCreateInputSurface(msg);
+ handled = true;
+ break;
+ }
+
case ACodec::kWhatStart:
{
onStart();
@@ -3310,6 +3338,32 @@ bool ACodec::LoadedState::onConfigureComponent(
return true;
}
+void ACodec::LoadedState::onCreateInputSurface(
+ const sp<AMessage> &msg) {
+ ALOGV("onCreateInputSurface");
+
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatInputSurfaceCreated);
+
+ sp<IGraphicBufferProducer> bufferProducer;
+ status_t err;
+
+ err = mCodec->mOMX->createInputSurface(mCodec->mNode, kPortIndexInput,
+ &bufferProducer);
+ if (err == OK) {
+ notify->setObject("input-surface",
+ new BufferProducerWrapper(bufferProducer));
+ } else {
+ // Can't use mCodec->signalError() here -- MediaCodec won't forward
+ // the error through because it's in the "configured" state. We
+ // send a kWhatInputSurfaceCreated with an error value instead.
+ ALOGE("[%s] onCreateInputSurface returning error %d",
+ mCodec->mComponentName.c_str(), err);
+ notify->setInt32("err", err);
+ }
+ notify->post();
+}
+
void ACodec::LoadedState::onStart() {
ALOGV("onStart");
@@ -3484,6 +3538,17 @@ void ACodec::ExecutingState::resume() {
mActive = true;
}
+void ACodec::ExecutingState::onSignalEndOfInputStream() {
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatSignaledInputEOS);
+
+ status_t err = mCodec->mOMX->signalEndOfInputStream(mCodec->mNode);
+ if (err != OK) {
+ notify->setInt32("err", err);
+ }
+ notify->post();
+}
+
void ACodec::ExecutingState::stateEntered() {
ALOGV("[%s] Now Executing", mCodec->mComponentName.c_str());
@@ -3573,6 +3638,13 @@ bool ACodec::ExecutingState::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ case ACodec::kWhatSignalEndOfInputStream:
+ {
+ onSignalEndOfInputStream();
+ handled = true;
+ break;
+ }
+
default:
handled = BaseState::onMessageReceived(msg);
break;
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 83be0fd..79ea04c 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -30,6 +30,7 @@
#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/ACodec.h>
+#include <media/stagefright/BufferProducerWrapper.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/NativeWindowWrapper.h>
@@ -62,6 +63,7 @@ MediaCodec::MediaCodec(const sp<ALooper> &looper)
: mState(UNINITIALIZED),
mLooper(looper),
mCodec(new ACodec),
+ mReplyID(0),
mFlags(0),
mSoftRenderer(NULL),
mDequeueInputTimeoutGeneration(0),
@@ -154,6 +156,28 @@ status_t MediaCodec::configure(
return PostAndAwaitResponse(msg, &response);
}
+status_t MediaCodec::createInputSurface(
+ sp<IGraphicBufferProducer>* bufferProducer) {
+ sp<AMessage> msg = new AMessage(kWhatCreateInputSurface, id());
+
+ // TODO(fadden): require MediaFormat colorFormat == AndroidOpaque
+
+ sp<AMessage> response;
+ status_t err = PostAndAwaitResponse(msg, &response);
+ if (err == NO_ERROR) {
+ // unwrap the sp<IGraphicBufferProducer>
+ sp<RefBase> obj;
+ bool found = response->findObject("input-surface", &obj);
+ CHECK(found);
+ sp<BufferProducerWrapper> wrapper(
+ static_cast<BufferProducerWrapper*>(obj.get()));
+ *bufferProducer = wrapper->getBufferProducer();
+ } else {
+ ALOGW("createInputSurface failed, err=%d", err);
+ }
+ return err;
+}
+
status_t MediaCodec::start() {
sp<AMessage> msg = new AMessage(kWhatStart, id());
@@ -232,6 +256,8 @@ status_t MediaCodec::queueSecureInputBuffer(
}
status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
+ // TODO(fadden): fail if an input Surface has been configured
+
sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, id());
msg->setInt64("timeoutUs", timeoutUs);
@@ -288,6 +314,13 @@ status_t MediaCodec::releaseOutputBuffer(size_t index) {
return PostAndAwaitResponse(msg, &response);
}
+status_t MediaCodec::signalEndOfInputStream() {
+ sp<AMessage> msg = new AMessage(kWhatSignalEndOfInputStream, id());
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
status_t MediaCodec::getOutputFormat(sp<AMessage> *format) const {
sp<AMessage> msg = new AMessage(kWhatGetOutputFormat, id());
@@ -575,6 +608,36 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ case ACodec::kWhatInputSurfaceCreated:
+ {
+ // response to ACodec::kWhatCreateInputSurface
+ status_t err = NO_ERROR;
+ sp<AMessage> response = new AMessage();
+ if (!msg->findInt32("err", &err)) {
+ sp<RefBase> obj;
+ msg->findObject("input-surface", &obj);
+ CHECK(obj != NULL);
+ response->setObject("input-surface", obj);
+ } else {
+ response->setInt32("err", err);
+ }
+ response->postReply(mReplyID);
+ break;
+ }
+
+ case ACodec::kWhatSignaledInputEOS:
+ {
+ // response to ACodec::kWhatSignalEndOfInputStream
+ sp<AMessage> response = new AMessage();
+ status_t err;
+ if (msg->findInt32("err", &err)) {
+ response->setInt32("err", err);
+ }
+ response->postReply(mReplyID);
+ break;
+ }
+
+
case ACodec::kWhatBuffersAllocated:
{
int32_t portIndex;
@@ -881,6 +944,25 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ case kWhatCreateInputSurface:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ // Must be configured, but can't have been started yet.
+ if (mState != CONFIGURED) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ mReplyID = replyID;
+ mCodec->initiateCreateInputSurface();
+ break;
+ }
+
case kWhatStart:
{
uint32_t replyID;
@@ -947,6 +1029,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
case kWhatDequeueInputBuffer:
{
+ // TODO(fadden): make this fail if we're using an input Surface
uint32_t replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
@@ -1093,6 +1176,24 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ case kWhatSignalEndOfInputStream:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != STARTED || (mFlags & kFlagStickyError)) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ mReplyID = replyID;
+ mCodec->signalEndOfInputStream();
+ break;
+ }
+
case kWhatGetBuffers:
{
uint32_t replyID;
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 7cdb793..ff72e0e 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -83,6 +83,12 @@ struct MuxOMX : public IOMX {
node_id node, OMX_U32 port_index,
const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer);
+ virtual status_t createInputSurface(
+ node_id node, OMX_U32 port_index,
+ sp<IGraphicBufferProducer> *bufferProducer);
+
+ virtual status_t signalEndOfInputStream(node_id node);
+
virtual status_t allocateBuffer(
node_id node, OMX_U32 port_index, size_t size,
buffer_id *buffer, void **buffer_data);
@@ -274,6 +280,18 @@ status_t MuxOMX::useGraphicBuffer(
node, port_index, graphicBuffer, buffer);
}
+status_t MuxOMX::createInputSurface(
+ node_id node, OMX_U32 port_index,
+ sp<IGraphicBufferProducer> *bufferProducer) {
+ status_t err = getOMX(node)->createInputSurface(
+ node, port_index, bufferProducer);
+ return err;
+}
+
+status_t MuxOMX::signalEndOfInputStream(node_id node) {
+ return getOMX(node)->signalEndOfInputStream(node);
+}
+
status_t MuxOMX::allocateBuffer(
node_id node, OMX_U32 port_index, size_t size,
buffer_id *buffer, void **buffer_data) {
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index 2c87b34..24b8d98 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -79,6 +79,12 @@ public:
node_id node, OMX_U32 port_index,
const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer);
+ virtual status_t createInputSurface(
+ node_id node, OMX_U32 port_index,
+ sp<IGraphicBufferProducer> *bufferProducer);
+
+ virtual status_t signalEndOfInputStream(node_id node);
+
virtual status_t allocateBuffer(
node_id node, OMX_U32 port_index, size_t size,
buffer_id *buffer, void **buffer_data);
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index 47ca579..67aba6b 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -27,6 +27,7 @@ namespace android {
class IOMXObserver;
struct OMXMaster;
+struct GraphicBufferSource;
struct OMXNodeInstance {
OMXNodeInstance(
@@ -65,6 +66,11 @@ struct OMXNodeInstance {
OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
OMX::buffer_id *buffer);
+ status_t createInputSurface(
+ OMX_U32 portIndex, sp<IGraphicBufferProducer> *bufferProducer);
+
+ status_t signalEndOfInputStream();
+
status_t allocateBuffer(
OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
void **buffer_data);
@@ -82,12 +88,18 @@ struct OMXNodeInstance {
OMX_U32 rangeOffset, OMX_U32 rangeLength,
OMX_U32 flags, OMX_TICKS timestamp);
+ status_t emptyDirectBuffer(
+ OMX_BUFFERHEADERTYPE *header,
+ OMX_U32 rangeOffset, OMX_U32 rangeLength,
+ OMX_U32 flags, OMX_TICKS timestamp);
+
status_t getExtensionIndex(
const char *parameterName, OMX_INDEXTYPE *index);
void onMessage(const omx_message &msg);
void onObserverDied(OMXMaster *master);
void onGetHandleFailed();
+ void onEvent(OMX_EVENTTYPE event, OMX_U32 arg1, OMX_U32 arg2);
static OMX_CALLBACKTYPE kCallbacks;
@@ -100,6 +112,13 @@ private:
sp<IOMXObserver> mObserver;
bool mDying;
+ // Lock only covers mGraphicBufferSource. We can't always use mLock
+ // because of rare instances where we'd end up locking it recursively.
+ Mutex mGraphicBufferSourceLock;
+ // Access this through getGraphicBufferSource().
+ sp<GraphicBufferSource> mGraphicBufferSource;
+
+
struct ActiveBuffer {
OMX_U32 mPortIndex;
OMX::buffer_id mID;
@@ -132,6 +151,11 @@ private:
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
+ status_t storeMetaDataInBuffers_l(OMX_U32 portIndex, OMX_BOOL enable);
+
+ sp<GraphicBufferSource> getGraphicBufferSource();
+ void setGraphicBufferSource(const sp<GraphicBufferSource>& bufferSource);
+
OMXNodeInstance(const OMXNodeInstance &);
OMXNodeInstance &operator=(const OMXNodeInstance &);
};
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index d7fbbbe..9129f08 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -2,6 +2,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
+ GraphicBufferSource.cpp \
OMX.cpp \
OMXMaster.cpp \
OMXNodeInstance.cpp \
@@ -19,6 +20,7 @@ LOCAL_SHARED_LIBRARIES := \
libmedia \
libutils \
libui \
+ libgui \
libcutils \
libstagefright_foundation \
libdl
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
new file mode 100644
index 0000000..f207954
--- /dev/null
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GraphicBufferSource"
+#include <utils/Log.h>
+
+#include <GraphicBufferSource.h>
+
+#include <OMX_Core.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <MetadataBufferType.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+
+static const bool EXTRA_CHECK = true;
+
+
+GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance,
+ uint32_t bufferWidth, uint32_t bufferHeight) :
+ mInitCheck(UNKNOWN_ERROR),
+ mNodeInstance(nodeInstance),
+ mExecuting(false),
+ mNumFramesAvailable(0),
+ mEndOfStream(false),
+ mEndOfStreamSent(false) {
+
+ ALOGV("GraphicBufferSource w=%u h=%u", bufferWidth, bufferHeight);
+
+ if (bufferWidth == 0 || bufferHeight == 0) {
+ ALOGE("Invalid dimensions %dx%d", bufferWidth, bufferHeight);
+ mInitCheck = BAD_VALUE;
+ return;
+ }
+
+ mBufferQueue = new BufferQueue(true);
+ mBufferQueue->setDefaultBufferSize(bufferWidth, bufferHeight);
+ mBufferQueue->setSynchronousMode(true);
+ mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER |
+ GRALLOC_USAGE_HW_TEXTURE);
+
+ // Note that we can't create an sp<...>(this) in a ctor that will not keep a
+ // reference once the ctor ends, as that would cause the refcount of 'this'
+ // dropping to 0 at the end of the ctor. Since all we need is a wp<...>
+ // that's what we create.
+ wp<BufferQueue::ConsumerListener> listener;
+ listener = static_cast<BufferQueue::ConsumerListener*>(this);
+
+ sp<BufferQueue::ConsumerListener> proxy;
+ proxy = new BufferQueue::ProxyConsumerListener(listener);
+
+ status_t err = mBufferQueue->consumerConnect(proxy);
+ if (err != NO_ERROR) {
+ ALOGE("Error connecting to BufferQueue: %s (%d)",
+ strerror(-err), err);
+ return;
+ }
+
+ mInitCheck = OK;
+}
+
+GraphicBufferSource::~GraphicBufferSource() {
+ ALOGV("~GraphicBufferSource");
+ status_t err = mBufferQueue->consumerDisconnect();
+ if (err != NO_ERROR) {
+ ALOGW("consumerDisconnect failed: %d", err);
+ }
+}
+
+void GraphicBufferSource::omxExecuting() {
+ Mutex::Autolock autoLock(mMutex);
+ ALOGV("--> executing; avail=%d, codec vec size=%zd",
+ mNumFramesAvailable, mCodecBuffers.size());
+ CHECK(!mExecuting);
+ mExecuting = true;
+
+ // Start by loading up as many buffers as possible. We want to do this,
+ // rather than just submit the first buffer, to avoid a degenerate case:
+ // if all BQ buffers arrive before we start executing, and we only submit
+ // one here, the other BQ buffers will just sit until we get notified
+ // that the codec buffer has been released. We'd then acquire and
+ // submit a single additional buffer, repeatedly, never using more than
+ // one codec buffer simultaneously. (We could instead try to submit
+ // all BQ buffers whenever any codec buffer is freed, but if we get the
+ // initial conditions right that will never be useful.)
+ while (mNumFramesAvailable && isCodecBufferAvailable_l()) {
+ fillCodecBuffer_l();
+ }
+
+ ALOGV("done loading initial frames, avail=%d", mNumFramesAvailable);
+
+ // If EOS has already been signaled, and there are no more frames to
+ // submit, try to send EOS now as well.
+ if (mEndOfStream && mNumFramesAvailable == 0) {
+ submitEndOfInputStream_l();
+ }
+}
+
+void GraphicBufferSource::omxIdling(){
+ Mutex::Autolock autoLock(mMutex);
+ ALOGV("--> idling");
+ if (!mExecuting) {
+ // Transition from "loading" to "idling". Nothing to do.
+ return;
+ }
+
+ ALOGV("Dropped down to idle, avail=%d eos=%d eosSent=%d",
+ mNumFramesAvailable, mEndOfStream, mEndOfStreamSent);
+
+ // Codec is no longer executing. Discard all codec-related state.
+ mCodecBuffers.clear();
+ // TODO: scan mCodecBuffers to verify that all mGraphicBuffer entries
+ // are null; complain if not
+
+ mExecuting = false;
+}
+
+void GraphicBufferSource::addCodecBuffer(OMX_BUFFERHEADERTYPE* header) {
+ Mutex::Autolock autoLock(mMutex);
+
+ if (mExecuting) {
+ // This should never happen -- buffers can only be allocated when
+ // transitioning from "loaded" to "idle".
+ ALOGE("addCodecBuffer: buffer added while executing");
+ return;
+ }
+
+ ALOGV("addCodecBuffer h=%p size=%lu p=%p",
+ header, header->nAllocLen, header->pBuffer);
+ CodecBuffer codecBuffer;
+ codecBuffer.mHeader = header;
+ mCodecBuffers.add(codecBuffer);
+}
+
+void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) {
+ Mutex::Autolock autoLock(mMutex);
+
+ CHECK(mExecuting); // could this happen if app stop()s early?
+
+ int cbi = findMatchingCodecBuffer_l(header);
+ if (cbi < 0) {
+ // This should never happen.
+ ALOGE("codecBufferEmptied: buffer not recognized (h=%p)", header);
+ return;
+ }
+
+ ALOGV("codecBufferEmptied h=%p size=%lu filled=%lu p=%p",
+ header, header->nAllocLen, header->nFilledLen,
+ header->pBuffer);
+ CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
+
+ // header->nFilledLen may not be the original value, so we can't compare
+ // that to zero to see of this was the EOS buffer. Instead we just
+ // see if the GraphicBuffer reference was null, which should only ever
+ // happen for EOS.
+ if (codecBuffer.mGraphicBuffer == NULL) {
+ CHECK(mEndOfStream);
+ // No GraphicBuffer to deal with, no additional input or output is
+ // expected, so just return.
+ return;
+ }
+
+ if (EXTRA_CHECK) {
+ // Pull the graphic buffer handle back out of the buffer, and confirm
+ // that it matches expectations.
+ OMX_U8* data = header->pBuffer;
+ buffer_handle_t bufferHandle;
+ memcpy(&bufferHandle, data + 4, sizeof(buffer_handle_t));
+ if (bufferHandle != codecBuffer.mGraphicBuffer->handle) {
+ // should never happen
+ ALOGE("codecBufferEmptied: buffer's handle is %p, expected %p",
+ bufferHandle, codecBuffer.mGraphicBuffer->handle);
+ CHECK(!"codecBufferEmptied: mismatched buffer");
+ }
+ }
+
+ // Find matching entry in our cached copy of the BufferQueue slots.
+ // If we find a match, release that slot. If we don't, the BufferQueue
+ // has dropped that GraphicBuffer, and there's nothing for us to release.
+ //
+ // (We could store "id" in CodecBuffer and avoid the slot search.)
+ int id;
+ for (id = 0; id < BufferQueue::NUM_BUFFER_SLOTS; id++) {
+ if (mBufferSlot[id] == NULL) {
+ continue;
+ }
+
+ if (mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) {
+ ALOGV("cbi %d matches bq slot %d, handle=%p",
+ cbi, id, mBufferSlot[id]->handle);
+
+ mBufferQueue->releaseBuffer(id, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR,
+ Fence::NO_FENCE);
+ break;
+ }
+ }
+ if (id == BufferQueue::NUM_BUFFER_SLOTS) {
+ ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d",
+ cbi);
+ }
+
+ // Mark the codec buffer as available by clearing the GraphicBuffer ref.
+ codecBuffer.mGraphicBuffer = NULL;
+
+ if (mNumFramesAvailable) {
+ // Fill this codec buffer.
+ CHECK(!mEndOfStream);
+ ALOGV("buffer freed, %d frames avail", mNumFramesAvailable);
+ fillCodecBuffer_l();
+ } else if (mEndOfStream) {
+ // No frames available, but EOS is pending, so use this buffer to
+ // send that.
+ ALOGV("buffer freed, EOS pending");
+ submitEndOfInputStream_l();
+ }
+ return;
+}
+
+status_t GraphicBufferSource::fillCodecBuffer_l() {
+ CHECK(mExecuting && mNumFramesAvailable > 0);
+ int cbi = findAvailableCodecBuffer_l();
+ if (cbi < 0) {
+ // No buffers available, bail.
+ ALOGV("fillCodecBuffer_l: no codec buffers, avail now %d",
+ mNumFramesAvailable);
+ } else {
+ ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%d",
+ mNumFramesAvailable);
+ BufferQueue::BufferItem item;
+ status_t err = mBufferQueue->acquireBuffer(&item);
+ if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+ // shouldn't happen
+ ALOGW("fillCodecBuffer_l: frame was not available");
+ return err;
+ } else if (err != OK) {
+ // now what? fake end-of-stream?
+ ALOGW("fillCodecBuffer_l: acquireBuffer returned err=%d", err);
+ return err;
+ }
+
+ mNumFramesAvailable--;
+
+ // Wait for it to become available.
+ err = item.mFence->waitForever(1000,
+ "GraphicBufferSource::fillCodecBuffer_l");
+ if (err != OK) {
+ ALOGW("failed to wait for buffer fence: %d", err);
+ // keep going
+ }
+
+ // If this is the first time we're seeing this buffer, add it to our
+ // slot table.
+ if (item.mGraphicBuffer != NULL) {
+ ALOGV("fillCodecBuffer_l: setting mBufferSlot %d", item.mBuf);
+ mBufferSlot[item.mBuf] = item.mGraphicBuffer;
+ }
+
+ err = submitBuffer_l(mBufferSlot[item.mBuf], item.mTimestamp, cbi);
+ if (err != OK) {
+ ALOGV("submitBuffer_l failed, releasing bq buf %d", item.mBuf);
+ mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ } else {
+ ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi);
+ }
+ }
+
+ return OK;
+}
+
+void GraphicBufferSource::signalEndOfInputStream() {
+ Mutex::Autolock autoLock(mMutex);
+ ALOGV("signalEndOfInputStream: exec=%d avail=%d",
+ mExecuting, mNumFramesAvailable);
+
+ // Set the end-of-stream flag. If no frames are pending from the
+ // BufferQueue, and a codec buffer is available, and we're executing,
+ // we initiate the EOS from here. Otherwise, we'll let
+ // codecBufferEmptied() (or omxExecuting) do it.
+ //
+ // Note: if there are no pending frames and all codec buffers are
+ // available, we *must* submit the EOS from here or we'll just
+ // stall since no future events are expected.
+ mEndOfStream = true;
+
+ if (mExecuting && mNumFramesAvailable == 0) {
+ submitEndOfInputStream_l();
+ }
+}
+
+status_t GraphicBufferSource::submitBuffer_l(sp<GraphicBuffer>& graphicBuffer,
+ int64_t timestamp, int cbi) {
+ ALOGV("submitBuffer_l cbi=%d", cbi);
+ CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
+ codecBuffer.mGraphicBuffer = graphicBuffer;
+
+ OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader;
+ CHECK(header->nAllocLen >= 4 + sizeof(buffer_handle_t));
+ OMX_U8* data = header->pBuffer;
+ const OMX_U32 type = kMetadataBufferTypeGrallocSource;
+ buffer_handle_t handle = codecBuffer.mGraphicBuffer->handle;
+ memcpy(data, &type, 4);
+ memcpy(data + 4, &handle, sizeof(buffer_handle_t));
+
+ status_t err = mNodeInstance->emptyDirectBuffer(header, 0,
+ 4 + sizeof(buffer_handle_t), OMX_BUFFERFLAG_ENDOFFRAME,
+ timestamp);
+ if (err != OK) {
+ ALOGW("WARNING: emptyDirectBuffer failed: 0x%x", err);
+ codecBuffer.mGraphicBuffer = NULL;
+ return err;
+ }
+
+ ALOGV("emptyDirectBuffer succeeded, h=%p p=%p bufhandle=%p",
+ header, header->pBuffer, handle);
+ return OK;
+}
+
+void GraphicBufferSource::submitEndOfInputStream_l() {
+ CHECK(mEndOfStream);
+ if (mEndOfStreamSent) {
+ ALOGV("EOS already sent");
+ return;
+ }
+
+ int cbi = findAvailableCodecBuffer_l();
+ if (cbi < 0) {
+ ALOGV("submitEndOfInputStream_l: no codec buffers available");
+ return;
+ }
+
+ // We reject any additional incoming graphic buffers, so there's no need
+ // to stick a placeholder into codecBuffer.mGraphicBuffer to mark it as
+ // in-use.
+ CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
+
+ OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader;
+ if (EXTRA_CHECK) {
+ // Guard against implementations that don't check nFilledLen.
+ size_t fillLen = 4 + sizeof(buffer_handle_t);
+ CHECK(header->nAllocLen >= fillLen);
+ OMX_U8* data = header->pBuffer;
+ memset(data, 0xcd, fillLen);
+ }
+
+ uint64_t timestamp = 0; // does this matter?
+
+ status_t err = mNodeInstance->emptyDirectBuffer(header, /*offset*/ 0,
+ /*length*/ 0, OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS,
+ timestamp);
+ if (err != OK) {
+ ALOGW("emptyDirectBuffer EOS failed: 0x%x", err);
+ } else {
+ ALOGV("submitEndOfInputStream_l: buffer submitted, header=%p cbi=%d",
+ header, cbi);
+ }
+}
+
+int GraphicBufferSource::findAvailableCodecBuffer_l() {
+ CHECK(mCodecBuffers.size() > 0);
+
+ for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) {
+ if (mCodecBuffers[i].mGraphicBuffer == NULL) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int GraphicBufferSource::findMatchingCodecBuffer_l(
+ const OMX_BUFFERHEADERTYPE* header) {
+ for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) {
+ if (mCodecBuffers[i].mHeader == header) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+// BufferQueue::ConsumerListener callback
+void GraphicBufferSource::onFrameAvailable() {
+ Mutex::Autolock autoLock(mMutex);
+
+ ALOGV("onFrameAvailable exec=%d avail=%d", mExecuting, mNumFramesAvailable);
+
+ if (mEndOfStream) {
+ // This should only be possible if a new buffer was queued after
+ // EOS was signaled, i.e. the app is misbehaving.
+ ALOGW("onFrameAvailable: EOS is set, ignoring frame");
+
+ BufferQueue::BufferItem item;
+ status_t err = mBufferQueue->acquireBuffer(&item);
+ if (err == OK) {
+ mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR, item.mFence);
+ }
+ return;
+ }
+
+ mNumFramesAvailable++;
+
+ if (mExecuting) {
+ fillCodecBuffer_l();
+ }
+}
+
+// BufferQueue::ConsumerListener callback
+void GraphicBufferSource::onBuffersReleased() {
+ Mutex::Autolock lock(mMutex);
+
+ uint32_t slotMask;
+ if (mBufferQueue->getReleasedBuffers(&slotMask) != NO_ERROR) {
+ ALOGW("onBuffersReleased: unable to get released buffer set");
+ slotMask = 0xffffffff;
+ }
+
+ ALOGV("onBuffersReleased: 0x%08x", slotMask);
+
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ if ((slotMask & 0x01) != 0) {
+ mBufferSlot[i] = NULL;
+ }
+ slotMask >>= 1;
+ }
+}
+
+} // namespace android
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
new file mode 100644
index 0000000..6d49f96
--- /dev/null
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GRAPHIC_BUFFER_SOURCE_H_
+
+#define GRAPHIC_BUFFER_SOURCE_H_
+
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/BufferQueue.h>
+#include <utils/RefBase.h>
+
+#include <OMX_Core.h>
+#include "../include/OMXNodeInstance.h"
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+/*
+ * This class is used to feed OMX codecs from a Surface via BufferQueue.
+ *
+ * Instances of the class don't run on a dedicated thread. Instead,
+ * various events trigger data movement:
+ *
+ * - Availability of a new frame of data from the BufferQueue (notified
+ * via the onFrameAvailable callback).
+ * - The return of a codec buffer (via OnEmptyBufferDone).
+ * - Application signaling end-of-stream.
+ * - Transition to or from "executing" state.
+ *
+ * Frames of data (and, perhaps, the end-of-stream indication) can arrive
+ * before the codec is in the "executing" state, so we need to queue
+ * things up until we're ready to go.
+ */
+class GraphicBufferSource : public BufferQueue::ConsumerListener {
+public:
+ GraphicBufferSource(OMXNodeInstance* nodeInstance,
+ uint32_t bufferWidth, uint32_t bufferHeight);
+ virtual ~GraphicBufferSource();
+
+ // We can't throw an exception if the constructor fails, so we just set
+ // this and require that the caller test the value.
+ status_t initCheck() const {
+ return mInitCheck;
+ }
+
+ // Returns the handle to the producer side of the BufferQueue. Buffers
+ // queued on this will be received by GraphicBufferSource.
+ sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
+ return mBufferQueue;
+ }
+
+ // This is called when OMX transitions to OMX_StateExecuting, which means
+ // we can start handing it buffers. If we already have buffers of data
+ // sitting in the BufferQueue, this will send them to the codec.
+ void omxExecuting();
+
+ // This is called when OMX transitions to OMX_StateIdle. If we were
+ // previously executing, this means we're about to be shut down. (We
+ // also enter Idle on the way up.)
+ void omxIdling();
+
+ // A "codec buffer", i.e. a buffer that can be used to pass data into
+ // the encoder, has been allocated. (This call does not call back into
+ // OMXNodeInstance.)
+ void addCodecBuffer(OMX_BUFFERHEADERTYPE* header);
+
+ // Called from OnEmptyBufferDone. If we have a BQ buffer available,
+ // fill it with a new frame of data; otherwise, just mark it as available.
+ void codecBufferEmptied(OMX_BUFFERHEADERTYPE* header);
+
+ // This is called after the last input frame has been submitted. We
+ // need to submit an empty buffer with the EOS flag set. If we don't
+ // have a codec buffer ready, we just set the mEndOfStream flag.
+ void signalEndOfInputStream();
+
+protected:
+ // BufferQueue::ConsumerListener interface, called when a new frame of
+ // data is available. If we're executing and a codec buffer is
+ // available, we acquire the buffer, copy the GraphicBuffer reference
+ // into the codec buffer, and call Empty[This]Buffer. If we're not yet
+ // executing or there's no codec buffer available, we just increment
+ // mNumFramesAvailable and return.
+ virtual void onFrameAvailable();
+
+ // BufferQueue::ConsumerListener interface, called when the client has
+ // released one or more GraphicBuffers. We clear out the appropriate
+ // set of mBufferSlot entries.
+ virtual void onBuffersReleased();
+
+private:
+ // Keep track of codec input buffers. They may either be available
+ // (mGraphicBuffer == NULL) or in use by the codec.
+ struct CodecBuffer {
+ OMX_BUFFERHEADERTYPE* mHeader;
+ sp<GraphicBuffer> mGraphicBuffer;
+ };
+
+ // Returns the index of an available codec buffer. If none are
+ // available, returns -1. Mutex must be held by caller.
+ int findAvailableCodecBuffer_l();
+
+ // Returns true if a codec buffer is available.
+ bool isCodecBufferAvailable_l() {
+ return findAvailableCodecBuffer_l() >= 0;
+ }
+
+ // Finds the mCodecBuffers entry that matches. Returns -1 if not found.
+ int findMatchingCodecBuffer_l(const OMX_BUFFERHEADERTYPE* header);
+
+ // Fills a codec buffer with a frame from the BufferQueue. This must
+ // only be called when we know that a frame of data is ready (i.e. we're
+ // in the onFrameAvailable callback, or if we're in codecBufferEmptied
+ // and mNumFramesAvailable is nonzero). Returns without doing anything if
+ // we don't have a codec buffer available.
+ status_t fillCodecBuffer_l();
+
+ // Marks the mCodecBuffers entry as in-use, copies the GraphicBuffer
+ // reference into the codec buffer, and submits the data to the codec.
+ status_t submitBuffer_l(sp<GraphicBuffer>& graphicBuffer,
+ int64_t timestamp, int cbi);
+
+ // Submits an empty buffer, with the EOS flag set. Returns without
+ // doing anything if we don't have a codec buffer available.
+ void submitEndOfInputStream_l();
+
+ // Lock, covers all member variables.
+ mutable Mutex mMutex;
+
+ // Used to report constructor failure.
+ status_t mInitCheck;
+
+ // Pointer back to the object that contains us. We send buffers here.
+ OMXNodeInstance* mNodeInstance;
+
+ // Set by omxExecuting() / omxIdling().
+ bool mExecuting;
+
+ // We consume graphic buffers from this.
+ sp<BufferQueue> mBufferQueue;
+
+ // Number of frames pending in BufferQueue that haven't yet been
+ // forwarded to the codec.
+ size_t mNumFramesAvailable;
+
+ // Set to true if we want to send end-of-stream after we run out of
+ // frames in BufferQueue.
+ bool mEndOfStream;
+ bool mEndOfStreamSent;
+
+ // Cache of GraphicBuffers from the buffer queue. When the codec
+ // is done processing a GraphicBuffer, we can use this to map back
+ // to a slot number.
+ sp<GraphicBuffer> mBufferSlot[BufferQueue::NUM_BUFFER_SLOTS];
+
+ // Tracks codec buffers.
+ Vector<CodecBuffer> mCodecBuffers;
+
+ DISALLOW_EVIL_CONSTRUCTORS(GraphicBufferSource);
+};
+
+} // namespace android
+
+#endif // GRAPHIC_BUFFER_SOURCE_H_
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 29bc733..3987ead 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -345,6 +345,17 @@ status_t OMX::useGraphicBuffer(
port_index, graphicBuffer, buffer);
}
+status_t OMX::createInputSurface(
+ node_id node, OMX_U32 port_index,
+ sp<IGraphicBufferProducer> *bufferProducer) {
+ return findInstance(node)->createInputSurface(
+ port_index, bufferProducer);
+}
+
+status_t OMX::signalEndOfInputStream(node_id node) {
+ return findInstance(node)->signalEndOfInputStream();
+}
+
status_t OMX::allocateBuffer(
node_id node, OMX_U32 port_index, size_t size,
buffer_id *buffer, void **buffer_data) {
@@ -393,6 +404,9 @@ OMX_ERRORTYPE OMX::OnEvent(
OMX_IN OMX_PTR pEventData) {
ALOGV("OnEvent(%d, %ld, %ld)", eEvent, nData1, nData2);
+ // Forward to OMXNodeInstance.
+ findInstance(node)->onEvent(eEvent, nData1, nData2);
+
omx_message msg;
msg.type = omx_message::EVENT;
msg.node = node;
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index bff3def..6c2c33b 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -20,14 +20,18 @@
#include "../include/OMXNodeInstance.h"
#include "OMXMaster.h"
+#include "GraphicBufferSource.h"
#include <OMX_Component.h>
#include <binder/IMemory.h>
+#include <gui/BufferQueue.h>
#include <HardwareAPI.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaErrors.h>
+static const OMX_U32 kPortIndexInput = 0;
+
namespace android {
struct BufferMeta {
@@ -100,6 +104,17 @@ void OMXNodeInstance::setHandle(OMX::node_id node_id, OMX_HANDLETYPE handle) {
mHandle = handle;
}
+sp<GraphicBufferSource> OMXNodeInstance::getGraphicBufferSource() {
+ Mutex::Autolock autoLock(mGraphicBufferSourceLock);
+ return mGraphicBufferSource;
+}
+
+void OMXNodeInstance::setGraphicBufferSource(
+ const sp<GraphicBufferSource>& bufferSource) {
+ Mutex::Autolock autoLock(mGraphicBufferSourceLock);
+ mGraphicBufferSource = bufferSource;
+}
+
OMX *OMXNodeInstance::owner() {
return mOwner;
}
@@ -354,7 +369,12 @@ status_t OMXNodeInstance::storeMetaDataInBuffers(
OMX_U32 portIndex,
OMX_BOOL enable) {
Mutex::Autolock autolock(mLock);
+ return storeMetaDataInBuffers_l(portIndex, enable);
+}
+status_t OMXNodeInstance::storeMetaDataInBuffers_l(
+ OMX_U32 portIndex,
+ OMX_BOOL enable) {
OMX_INDEXTYPE index;
OMX_STRING name = const_cast<OMX_STRING>(
"OMX.google.android.index.storeMetaDataInBuffers");
@@ -411,6 +431,11 @@ status_t OMXNodeInstance::useBuffer(
addActiveBuffer(portIndex, *buffer);
+ sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
+ if (bufferSource != NULL && portIndex == kPortIndexInput) {
+ bufferSource->addCodecBuffer(header);
+ }
+
return OK;
}
@@ -530,6 +555,60 @@ status_t OMXNodeInstance::useGraphicBuffer(
return OK;
}
+status_t OMXNodeInstance::createInputSurface(
+ OMX_U32 portIndex, sp<IGraphicBufferProducer> *bufferProducer) {
+ Mutex::Autolock autolock(mLock);
+ status_t err;
+
+ const sp<GraphicBufferSource>& surfaceCheck = getGraphicBufferSource();
+ if (surfaceCheck != NULL) {
+ return ALREADY_EXISTS;
+ }
+
+ // Input buffers will hold meta-data (gralloc references).
+ err = storeMetaDataInBuffers_l(portIndex, OMX_TRUE);
+ if (err != OK) {
+ return err;
+ }
+
+ // Retrieve the width and height of the graphic buffer, set when the
+ // codec was configured.
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 0;
+ def.nVersion.s.nRevision = 0;
+ def.nVersion.s.nStep = 0;
+ def.nPortIndex = portIndex;
+ OMX_ERRORTYPE oerr = OMX_GetParameter(
+ mHandle, OMX_IndexParamPortDefinition, &def);
+ CHECK(oerr == OMX_ErrorNone);
+
+ GraphicBufferSource* bufferSource = new GraphicBufferSource(
+ this, def.format.video.nFrameWidth, def.format.video.nFrameHeight);
+ if ((err = bufferSource->initCheck()) != OK) {
+ delete bufferSource;
+ return err;
+ }
+ setGraphicBufferSource(bufferSource);
+
+ *bufferProducer = bufferSource->getIGraphicBufferProducer();
+ return OK;
+}
+
+status_t OMXNodeInstance::signalEndOfInputStream() {
+ // For non-Surface input, the MediaCodec should convert the call to a
+ // pair of requests (dequeue input buffer, queue input buffer with EOS
+ // flag set). Seems easier than doing the equivalent from here.
+ sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
+ if (bufferSource == NULL) {
+ ALOGW("signalEndOfInputStream should only be used with Surface input");
+ return INVALID_OPERATION;
+ };
+ bufferSource->signalEndOfInputStream();
+ return OK;
+}
+
status_t OMXNodeInstance::allocateBuffer(
OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
void **buffer_data) {
@@ -560,6 +639,11 @@ status_t OMXNodeInstance::allocateBuffer(
addActiveBuffer(portIndex, *buffer);
+ sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
+ if (bufferSource != NULL && portIndex == kPortIndexInput) {
+ bufferSource->addCodecBuffer(header);
+ }
+
return OK;
}
@@ -592,6 +676,11 @@ status_t OMXNodeInstance::allocateBufferWithBackup(
addActiveBuffer(portIndex, *buffer);
+ sp<GraphicBufferSource> bufferSource(getGraphicBufferSource());
+ if (bufferSource != NULL && portIndex == kPortIndexInput) {
+ bufferSource->addCodecBuffer(header);
+ }
+
return OK;
}
@@ -646,6 +735,26 @@ status_t OMXNodeInstance::emptyBuffer(
return StatusFromOMXError(err);
}
+// like emptyBuffer, but the data is already in header->pBuffer
+status_t OMXNodeInstance::emptyDirectBuffer(
+ OMX_BUFFERHEADERTYPE *header,
+ OMX_U32 rangeOffset, OMX_U32 rangeLength,
+ OMX_U32 flags, OMX_TICKS timestamp) {
+ Mutex::Autolock autoLock(mLock);
+
+ header->nFilledLen = rangeLength;
+ header->nOffset = rangeOffset;
+ header->nFlags = flags;
+ header->nTimeStamp = timestamp;
+
+ OMX_ERRORTYPE err = OMX_EmptyThisBuffer(mHandle, header);
+ if (err != OMX_ErrorNone) {
+ ALOGW("emptyDirectBuffer failed, OMX err=0x%x", err);
+ }
+
+ return StatusFromOMXError(err);
+}
+
status_t OMXNodeInstance::getExtensionIndex(
const char *parameterName, OMX_INDEXTYPE *index) {
Mutex::Autolock autoLock(mLock);
@@ -682,6 +791,22 @@ void OMXNodeInstance::onGetHandleFailed() {
delete this;
}
+// OMXNodeInstance::OnEvent calls OMX::OnEvent, which then calls here.
+// Don't try to acquire mLock here -- in rare circumstances this will hang.
+void OMXNodeInstance::onEvent(
+ OMX_EVENTTYPE event, OMX_U32 arg1, OMX_U32 arg2) {
+ const sp<GraphicBufferSource>& bufferSource(getGraphicBufferSource());
+
+ if (bufferSource != NULL && event == OMX_EventCmdComplete &&
+ arg1 == OMX_CommandStateSet) {
+ if (arg2 == OMX_StateExecuting) {
+ bufferSource->omxExecuting();
+ } else if (arg2 == OMX_StateIdle) {
+ bufferSource->omxIdling();
+ }
+ }
+}
+
// static
OMX_ERRORTYPE OMXNodeInstance::OnEvent(
OMX_IN OMX_HANDLETYPE hComponent,
@@ -707,6 +832,17 @@ OMX_ERRORTYPE OMXNodeInstance::OnEmptyBufferDone(
if (instance->mDying) {
return OMX_ErrorNone;
}
+ const sp<GraphicBufferSource>& bufferSource(
+ instance->getGraphicBufferSource());
+ if (bufferSource != NULL) {
+ bufferSource->codecBufferEmptied(pBuffer);
+
+ // This is one of the buffers used exclusively by GraphicBufferSource.
+ // Don't dispatch a message back to ACodec, since it doesn't
+ // know that anyone asked to have the buffer emptied and will
+ // be very confused.
+ return OMX_ErrorNone;
+ }
return instance->owner()->OnEmptyBufferDone(instance->nodeID(), pBuffer);
}