diff options
-rw-r--r-- | include/media/stagefright/MediaBuffer.h | 6 | ||||
-rw-r--r-- | include/media/stagefright/MetaData.h | 3 | ||||
-rw-r--r-- | include/media/stagefright/OMXCodec.h | 21 | ||||
-rw-r--r-- | media/libstagefright/AwesomePlayer.cpp | 61 | ||||
-rw-r--r-- | media/libstagefright/MediaBuffer.cpp | 29 | ||||
-rw-r--r-- | media/libstagefright/OMXCodec.cpp | 284 |
6 files changed, 378 insertions, 26 deletions
diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h index 339e6fb..c1c4f94 100644 --- a/include/media/stagefright/MediaBuffer.h +++ b/include/media/stagefright/MediaBuffer.h @@ -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; diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h index d2bd9f2..423f385 100644 --- a/include/media/stagefright/MetaData.h +++ b/include/media/stagefright/MetaData.h @@ -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 { diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index 1d94160..6fef2e7 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -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); diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index ad6cb5b..d172eef 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -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 - diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp index b973745..cbccd31 100644 --- a/media/libstagefright/MediaBuffer.cpp +++ b/media/libstagefright/MediaBuffer.cpp @@ -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 - diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 4be1b38..560d0f1 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -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); |