Merge "Add decode-to-ANativeWindow support to Stagefright."

This commit is contained in:
Jamie Gennis
2010-10-20 11:38:28 -07:00
committed by Android (Google) Code Review
6 changed files with 378 additions and 26 deletions

View File

@@ -25,6 +25,7 @@
namespace android {
class GraphicBuffer;
class MediaBuffer;
class MediaBufferObserver;
class MetaData;
@@ -48,6 +49,8 @@ public:
MediaBuffer(size_t size);
MediaBuffer(const sp<GraphicBuffer>& graphicBuffer);
// Decrements the reference count and returns the buffer to its
// associated MediaBufferGroup if the reference count drops to 0.
void release();
@@ -63,6 +66,8 @@ public:
void set_range(size_t offset, size_t length);
sp<GraphicBuffer> graphicBuffer() const;
sp<MetaData> meta_data();
// Clears meta data and resets the range to the full extent.
@@ -94,6 +99,7 @@ private:
void *mData;
size_t mSize, mRangeOffset, mRangeLength;
sp<GraphicBuffer> mGraphicBuffer;
bool mOwnsData;

View File

@@ -99,6 +99,9 @@ enum {
kKeyValidSamples = 'valD', // int32_t
kKeyIsUnreadable = 'unre', // bool (int32_t)
// An indication that a video buffer has been rendered.
kKeyRendered = 'rend', // bool (int32_t)
};
enum {

View File

@@ -18,6 +18,7 @@
#define OMX_CODEC_H_
#include <android/native_window.h>
#include <media/IOMX.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaSource.h>
@@ -44,7 +45,8 @@ struct OMXCodec : public MediaSource,
const sp<MetaData> &meta, bool createEncoder,
const sp<MediaSource> &source,
const char *matchComponentName = NULL,
uint32_t flags = 0);
uint32_t flags = 0,
const sp<ANativeWindow> &nativeWindow = NULL);
static void setComponentRole(
const sp<IOMX> &omx, IOMX::node_id node, bool isEncoder,
@@ -114,6 +116,7 @@ private:
struct BufferInfo {
IOMX::buffer_id mBuffer;
bool mOwnedByComponent;
bool mOwnedByNativeWindow;
sp<IMemory> mMem;
size_t mSize;
void *mData;
@@ -159,13 +162,21 @@ private:
bool mPaused;
sp<ANativeWindow> mNativeWindow;
// The index in each of the mPortBuffers arrays of the buffer that will be
// submitted to OMX next. This only applies when using buffers from a
// native window.
size_t mNextNativeBufferIndex[2];
// A list of indices into mPortStatus[kPortIndexOutput] filled with data.
List<size_t> mFilledBuffers;
Condition mBufferFilled;
OMXCodec(const sp<IOMX> &omx, IOMX::node_id node, uint32_t quirks,
bool isEncoder, const char *mime, const char *componentName,
const sp<MediaSource> &source);
const sp<MediaSource> &source,
const sp<ANativeWindow> &nativeWindow);
void addCodecSpecificData(const void *data, size_t size);
void clearCodecSpecificData();
@@ -216,6 +227,11 @@ private:
status_t allocateBuffers();
status_t allocateBuffersOnPort(OMX_U32 portIndex);
status_t allocateOutputBuffersFromNativeWindow();
status_t queueBufferToNativeWindow(BufferInfo *info);
status_t cancelBufferToNativeWindow(BufferInfo *info);
BufferInfo* dequeueBufferFromNativeWindow();
status_t freeBuffersOnPort(
OMX_U32 portIndex, bool onlyThoseWeOwn = false);
@@ -250,6 +266,7 @@ private:
status_t init();
void initOutputFormat(const sp<MetaData> &inputFormat);
status_t initNativeWindow();
void dumpPortStatus(OMX_U32 portIndex);

View File

@@ -194,6 +194,35 @@ void AwesomeLocalRenderer::init(
}
}
struct AwesomeNativeWindowRenderer : public AwesomeRenderer {
AwesomeNativeWindowRenderer(const sp<ANativeWindow> &nativeWindow)
: mNativeWindow(nativeWindow) {
}
virtual void render(MediaBuffer *buffer) {
status_t err = mNativeWindow->queueBuffer(
mNativeWindow.get(), buffer->graphicBuffer().get());
if (err != 0) {
LOGE("queueBuffer failed with error %s (%d)", strerror(-err),
-err);
return;
}
sp<MetaData> metaData = buffer->meta_data();
metaData->setInt32(kKeyRendered, 1);
}
protected:
virtual ~AwesomeNativeWindowRenderer() {}
private:
sp<ANativeWindow> mNativeWindow;
AwesomeNativeWindowRenderer(const AwesomeNativeWindowRenderer &);
AwesomeNativeWindowRenderer &operator=(
const AwesomeNativeWindowRenderer &);
};
AwesomePlayer::AwesomePlayer()
: mQueueStarted(false),
mTimeSource(NULL),
@@ -805,16 +834,25 @@ void AwesomePlayer::initRenderer_l() {
IPCThreadState::self()->flushCommands();
if (mSurface != NULL) {
// Other decoders are instantiated locally and as a consequence
// allocate their buffers in local address space.
mVideoRenderer = new AwesomeLocalRenderer(
false, // previewOnly
component,
(OMX_COLOR_FORMATTYPE)format,
mISurface,
mSurface,
mVideoWidth, mVideoHeight,
decodedWidth, decodedHeight);
if (strncmp(component, "OMX.", 4) == 0) {
// Hardware decoders avoid the CPU color conversion by decoding
// directly to ANativeBuffers, so we must use a renderer that
// just pushes those buffers to the ANativeWindow.
mVideoRenderer = new AwesomeNativeWindowRenderer(mSurface);
} else {
// Other decoders are instantiated locally and as a consequence
// allocate their buffers in local address space. This renderer
// then performs a color conversion and copy to get the data
// into the ANativeBuffer.
mVideoRenderer = new AwesomeLocalRenderer(
false, // previewOnly
component,
(OMX_COLOR_FORMATTYPE)format,
mISurface,
mSurface,
mVideoWidth, mVideoHeight,
decodedWidth, decodedHeight);
}
} else {
// Our OMX codecs allocate buffers on the media_server side
// therefore they require a remote IOMXRenderer that knows how
@@ -1054,7 +1092,7 @@ status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {
mClient.interface(), mVideoTrack->getFormat(),
false, // createEncoder
mVideoTrack,
NULL, flags);
NULL, flags, mSurface);
if (mVideoSource != NULL) {
int64_t durationUs;
@@ -1811,4 +1849,3 @@ void AwesomePlayer::postAudioSeekComplete() {
}
} // namespace android

View File

@@ -25,6 +25,8 @@
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MetaData.h>
#include <ui/GraphicBuffer.h>
namespace android {
// XXX make this truly atomic.
@@ -61,6 +63,20 @@ MediaBuffer::MediaBuffer(size_t size)
mOriginal(NULL) {
}
MediaBuffer::MediaBuffer(const sp<GraphicBuffer>& graphicBuffer)
: mObserver(NULL),
mNextBuffer(NULL),
mRefCount(0),
mData(NULL),
mSize(1),
mRangeOffset(0),
mRangeLength(mSize),
mGraphicBuffer(graphicBuffer),
mOwnsData(false),
mMetaData(new MetaData),
mOriginal(NULL) {
}
void MediaBuffer::release() {
if (mObserver == NULL) {
CHECK_EQ(mRefCount, 0);
@@ -92,10 +108,12 @@ void MediaBuffer::add_ref() {
}
void *MediaBuffer::data() const {
CHECK(mGraphicBuffer == NULL);
return mData;
}
size_t MediaBuffer::size() const {
CHECK(mGraphicBuffer == NULL);
return mSize;
}
@@ -108,15 +126,19 @@ size_t MediaBuffer::range_length() const {
}
void MediaBuffer::set_range(size_t offset, size_t length) {
if (offset + length > mSize) {
if ((mGraphicBuffer == NULL) && (offset + length > mSize)) {
LOGE("offset = %d, length = %d, mSize = %d", offset, length, mSize);
}
CHECK(offset + length <= mSize);
CHECK((mGraphicBuffer != NULL) || (offset + length <= mSize));
mRangeOffset = offset;
mRangeLength = length;
}
sp<GraphicBuffer> MediaBuffer::graphicBuffer() const {
return mGraphicBuffer;
}
sp<MetaData> MediaBuffer::meta_data() {
return mMetaData;
}
@@ -158,6 +180,8 @@ int MediaBuffer::refcount() const {
}
MediaBuffer *MediaBuffer::clone() {
CHECK_EQ(mGraphicBuffer, NULL);
MediaBuffer *buffer = new MediaBuffer(mData, mSize);
buffer->set_range(mRangeOffset, mRangeLength);
buffer->mMetaData = new MetaData(*mMetaData.get());
@@ -169,4 +193,3 @@ MediaBuffer *MediaBuffer::clone() {
}
} // namespace android

View File

@@ -39,6 +39,7 @@
#include <binder/MemoryDealer.h>
#include <binder/ProcessState.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/HardwareAPI.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDebug.h>
@@ -478,7 +479,8 @@ sp<MediaSource> OMXCodec::Create(
const sp<MetaData> &meta, bool createEncoder,
const sp<MediaSource> &source,
const char *matchComponentName,
uint32_t flags) {
uint32_t flags,
const sp<ANativeWindow> &nativeWindow) {
const char *mime;
bool success = meta->findCString(kKeyMIMEType, &mime);
CHECK(success);
@@ -534,7 +536,7 @@ sp<MediaSource> OMXCodec::Create(
sp<OMXCodec> codec = new OMXCodec(
omx, node, quirks,
createEncoder, mime, componentName,
source);
source, nativeWindow);
observer->setCodec(codec);
@@ -742,6 +744,15 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta, uint32_t flags) {
mQuirks &= ~kOutputBuffersAreUnreadable;
}
if (!mIsEncoder
&& !strncasecmp(mMIME, "video/", 6)
&& !strncmp(mComponentName, "OMX.", 4)) {
status_t err = initNativeWindow();
if (err != OK) {
return err;
}
}
return OK;
}
@@ -1429,7 +1440,8 @@ OMXCodec::OMXCodec(
bool isEncoder,
const char *mime,
const char *componentName,
const sp<MediaSource> &source)
const sp<MediaSource> &source,
const sp<ANativeWindow> &nativeWindow)
: mOMX(omx),
mOMXLivesLocally(omx->livesLocally(getpid())),
mNode(node),
@@ -1449,7 +1461,8 @@ OMXCodec::OMXCodec(
mTargetTimeUs(-1),
mSkipTimeUs(-1),
mLeftOverBuffer(NULL),
mPaused(false) {
mPaused(false),
mNativeWindow(nativeWindow) {
mPortStatus[kPortIndexInput] = ENABLED;
mPortStatus[kPortIndexOutput] = ENABLED;
@@ -1593,6 +1606,10 @@ status_t OMXCodec::allocateBuffers() {
}
status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) {
if (mNativeWindow != 0 && portIndex == kPortIndexOutput) {
return allocateOutputBuffersFromNativeWindow();
}
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = portIndex;
@@ -1685,6 +1702,178 @@ status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) {
return OK;
}
status_t OMXCodec::allocateOutputBuffersFromNativeWindow() {
// Get the number of buffers needed.
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = kPortIndexOutput;
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
if (err != OK) {
return err;
}
// Check that the color format is in the correct range.
CHECK(OMX_COLOR_FormatAndroidPrivateStart <= def.format.video.eColorFormat);
CHECK(def.format.video.eColorFormat < OMX_COLOR_FormatAndroidPrivateEnd);
err = native_window_set_buffers_geometry(
mNativeWindow.get(),
def.format.video.nFrameWidth,
def.format.video.nFrameHeight,
def.format.video.eColorFormat
- OMX_COLOR_FormatAndroidPrivateStart);
if (err != 0) {
LOGE("native_window_set_buffers_geometry failed: %s (%d)",
strerror(-err), -err);
return err;
}
// Increase the buffer count by one to allow for the ANativeWindow to hold
// on to one of the buffers.
def.nBufferCountActual++;
err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
if (err != OK) {
return err;
}
// Set up the native window.
// XXX TODO: Get the gralloc usage flags from the OMX plugin!
err = native_window_set_usage(
mNativeWindow.get(), GRALLOC_USAGE_HW_TEXTURE);
if (err != 0) {
LOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
return err;
}
err = native_window_set_buffer_count(
mNativeWindow.get(), def.nBufferCountActual);
if (err != 0) {
LOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err),
-err);
return err;
}
// XXX TODO: Do something so the ANativeWindow knows that we'll need to get
// the same set of buffers.
CODEC_LOGI("allocating %lu buffers from a native window of size %lu on "
"output port", def.nBufferCountActual, def.nBufferSize);
// Dequeue buffers and send them to OMX
OMX_U32 i;
for (i = 0; i < def.nBufferCountActual; i++) {
android_native_buffer_t* buf;
err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf);
if (err != 0) {
LOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
break;
}
sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));
IOMX::buffer_id bufferId;
err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer,
&bufferId);
if (err != 0) {
break;
}
CODEC_LOGV("registered graphic buffer with ID %p (pointer = %p)",
bufferId, graphicBuffer.get());
BufferInfo info;
info.mData = NULL;
info.mSize = def.nBufferSize;
info.mBuffer = bufferId;
info.mOwnedByComponent = false;
info.mOwnedByNativeWindow = false;
info.mMem = NULL;
info.mMediaBuffer = new MediaBuffer(graphicBuffer);
info.mMediaBuffer->setObserver(this);
mPortBuffers[kPortIndexOutput].push(info);
}
OMX_U32 cancelStart;
OMX_U32 cancelEnd;
if (err != 0) {
// If an error occurred while dequeuing we need to cancel any buffers
// that were dequeued.
cancelStart = 0;
cancelEnd = i;
} else {
// Return the last two buffers to the native window.
// XXX TODO: The number of buffers the native window owns should probably be
// queried from it when we put the native window in fixed buffer pool mode
// (which needs to be implemented). Currently it's hard-coded to 2.
cancelStart = def.nBufferCountActual - 2;
cancelEnd = def.nBufferCountActual;
}
for (OMX_U32 i = cancelStart; i < cancelEnd; i++) {
BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(i);
cancelBufferToNativeWindow(info);
}
return err;
}
status_t OMXCodec::cancelBufferToNativeWindow(BufferInfo *info) {
CHECK(!info->mOwnedByNativeWindow);
CODEC_LOGV("Calling cancelBuffer on buffer %p", info->mBuffer);
int err = mNativeWindow->cancelBuffer(
mNativeWindow.get(), info->mMediaBuffer->graphicBuffer().get());
if (err != 0) {
CODEC_LOGE("cancelBuffer failed w/ error 0x%08x", err);
setState(ERROR);
return err;
}
info->mOwnedByNativeWindow = true;
return OK;
}
OMXCodec::BufferInfo* OMXCodec::dequeueBufferFromNativeWindow() {
// Dequeue the next buffer from the native window.
android_native_buffer_t* buf;
int err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf);
if (err != 0) {
CODEC_LOGE("dequeueBuffer failed w/ error 0x%08x", err);
setState(ERROR);
return 0;
}
// Determine which buffer we just dequeued.
Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
BufferInfo *bufInfo = 0;
for (size_t i = 0; i < buffers->size(); i++) {
sp<GraphicBuffer> graphicBuffer = buffers->itemAt(i).
mMediaBuffer->graphicBuffer();
if (graphicBuffer->handle == buf->handle) {
bufInfo = &buffers->editItemAt(i);
break;
}
}
if (bufInfo == 0) {
CODEC_LOGE("dequeued unrecognized buffer: %p", buf);
setState(ERROR);
return 0;
}
// The native window no longer owns the buffer.
CHECK(bufInfo->mOwnedByNativeWindow);
bufInfo->mOwnedByNativeWindow = false;
return bufInfo;
}
void OMXCodec::on_message(const omx_message &msg) {
Mutex::Autolock autoLock(mLock);
@@ -1769,6 +1958,15 @@ void OMXCodec::on_message(const omx_message &msg) {
mOMX->freeBuffer(mNode, kPortIndexOutput, buffer);
CHECK_EQ(err, OK);
// Cancel the buffer if it belongs to an ANativeWindow.
if (info->mMediaBuffer != NULL) {
sp<GraphicBuffer> graphicBuffer = info->mMediaBuffer->graphicBuffer();
if (!info->mOwnedByNativeWindow && graphicBuffer != 0) {
cancelBufferToNativeWindow(info);
// Ignore any errors
}
}
buffers->removeAt(i);
#if 0
} else if (mPortStatus[kPortIndexOutput] == ENABLED
@@ -1797,8 +1995,10 @@ void OMXCodec::on_message(const omx_message &msg) {
}
MediaBuffer *buffer = info->mMediaBuffer;
bool isGraphicBuffer = buffer->graphicBuffer() != NULL;
if (msg.u.extended_buffer_data.range_offset
if (!isGraphicBuffer
&& msg.u.extended_buffer_data.range_offset
+ msg.u.extended_buffer_data.range_length
> buffer->size()) {
CODEC_LOGE(
@@ -1822,7 +2022,7 @@ void OMXCodec::on_message(const omx_message &msg) {
buffer->meta_data()->setInt32(kKeyIsCodecConfig, true);
}
if (mQuirks & kOutputBuffersAreUnreadable) {
if (isGraphicBuffer || mQuirks & kOutputBuffersAreUnreadable) {
buffer->meta_data()->setInt32(kKeyIsUnreadable, true);
}
@@ -2247,6 +2447,15 @@ status_t OMXCodec::freeBuffersOnPort(
// Make sure nobody but us owns this buffer at this point.
CHECK_EQ(info->mMediaBuffer->refcount(), 0);
// Cancel the buffer if it belongs to an ANativeWindow.
sp<GraphicBuffer> graphicBuffer = info->mMediaBuffer->graphicBuffer();
if (!info->mOwnedByNativeWindow && graphicBuffer != 0) {
status_t err = cancelBufferToNativeWindow(info);
if (err != OK) {
stickyErr = err;
}
}
info->mMediaBuffer->release();
}
@@ -2321,6 +2530,7 @@ void OMXCodec::enablePortAsync(OMX_U32 portIndex) {
CHECK_EQ(mPortStatus[portIndex], DISABLED);
mPortStatus[portIndex] = ENABLING;
CODEC_LOGV("sending OMX_CommandPortEnable(%ld)", portIndex);
status_t err =
mOMX->sendCommand(mNode, OMX_CommandPortEnable, portIndex);
CHECK_EQ(err, OK);
@@ -2346,7 +2556,10 @@ void OMXCodec::fillOutputBuffers() {
Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
for (size_t i = 0; i < buffers->size(); ++i) {
fillOutputBuffer(&buffers->editItemAt(i));
BufferInfo *info = &buffers->editItemAt(i);
if (!info->mOwnedByNativeWindow) {
fillOutputBuffer(&buffers->editItemAt(i));
}
}
}
@@ -2563,7 +2776,23 @@ void OMXCodec::fillOutputBuffer(BufferInfo *info) {
return;
}
CODEC_LOGV("Calling fill_buffer on buffer %p", info->mBuffer);
sp<GraphicBuffer> graphicBuffer = info->mMediaBuffer->graphicBuffer();
if (graphicBuffer != 0) {
// When using a native buffer we need to lock the buffer before giving
// it to OMX.
CHECK(!info->mOwnedByNativeWindow);
CODEC_LOGV("Calling lockBuffer on %p", info->mBuffer);
int err = mNativeWindow->lockBuffer(mNativeWindow.get(),
graphicBuffer.get());
if (err != 0) {
CODEC_LOGE("lockBuffer failed w/ error 0x%08x", err);
setState(ERROR);
return;
}
}
CODEC_LOGV("Calling fillBuffer on buffer %p", info->mBuffer);
status_t err = mOMX->fillBuffer(mNode, info->mBuffer);
if (err != OK) {
@@ -3146,7 +3375,32 @@ void OMXCodec::signalBufferReturned(MediaBuffer *buffer) {
if (info->mMediaBuffer == buffer) {
CHECK_EQ(mPortStatus[kPortIndexOutput], ENABLED);
fillOutputBuffer(info);
if (buffer->graphicBuffer() == 0) {
fillOutputBuffer(info);
} else {
sp<MetaData> metaData = info->mMediaBuffer->meta_data();
int32_t rendered = 0;
if (!metaData->findInt32(kKeyRendered, &rendered)) {
rendered = 0;
}
if (!rendered) {
status_t err = cancelBufferToNativeWindow(info);
if (err < 0) {
return;
}
} else {
info->mOwnedByNativeWindow = true;
}
// Dequeue the next buffer from the native window.
BufferInfo *nextBufInfo = dequeueBufferFromNativeWindow();
if (nextBufInfo == 0) {
return;
}
// Give the buffer to the OMX node to fill.
fillOutputBuffer(nextBufInfo);
}
return;
}
}
@@ -3479,6 +3733,18 @@ void OMXCodec::dumpPortStatus(OMX_U32 portIndex) {
printf("}\n");
}
status_t OMXCodec::initNativeWindow() {
// Enable use of a GraphicBuffer as the output for this node. This must
// happen before getting the IndexParamPortDefinition parameter because it
// will affect the pixel format that the node reports.
status_t err = mOMX->enableGraphicBuffers(mNode, kPortIndexOutput, OMX_TRUE);
if (err != 0) {
return err;
}
return OK;
}
void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) {
mOutputFormat = new MetaData;
mOutputFormat->setCString(kKeyDecoderComponent, mComponentName);