diff options
Diffstat (limited to 'media')
-rw-r--r-- | media/libmedia/Android.mk | 4 | ||||
-rw-r--r-- | media/libmedia/IEffect.cpp | 191 | ||||
-rw-r--r-- | media/libmedia/IEffectClient.cpp | 141 | ||||
-rw-r--r-- | media/libstagefright/Android.mk | 2 | ||||
-rw-r--r-- | media/libstagefright/CameraSource.cpp | 28 | ||||
-rw-r--r-- | media/libstagefright/OMXCodec.cpp | 4 | ||||
-rw-r--r-- | media/libstagefright/OggExtractor.cpp | 66 | ||||
-rw-r--r-- | media/libstagefright/codecs/on2/Android.mk | 4 | ||||
-rw-r--r-- | media/libstagefright/codecs/on2/dec/Android.mk | 16 | ||||
-rw-r--r-- | media/libstagefright/codecs/on2/dec/VPXDecoder.cpp | 230 | ||||
-rw-r--r-- | media/libstagefright/include/VPXDecoder.h | 60 |
11 files changed, 724 insertions, 22 deletions
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 3adabcc..29cd2ee 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -26,7 +26,9 @@ LOCAL_SRC_FILES:= \ MediaScannerClient.cpp \ autodetect.cpp \ IMediaDeathNotifier.cpp \ - MediaProfiles.cpp + MediaProfiles.cpp \ + IEffect.cpp \ + IEffectClient.cpp LOCAL_SHARED_LIBRARIES := \ libui libcutils libutils libbinder libsonivox libicuuc libexpat libsurfaceflinger_client libcamera_client diff --git a/media/libmedia/IEffect.cpp b/media/libmedia/IEffect.cpp new file mode 100644 index 0000000..8e3ac71 --- /dev/null +++ b/media/libmedia/IEffect.cpp @@ -0,0 +1,191 @@ +/* +** +** Copyright 2010, 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_NDEBUG 0 +#define LOG_TAG "IEffect" +#include <utils/Log.h> +#include <stdint.h> +#include <sys/types.h> +#include <binder/Parcel.h> +#include <media/IEffect.h> + +namespace android { + +enum { + ENABLE = IBinder::FIRST_CALL_TRANSACTION, + DISABLE, + COMMAND, + DISCONNECT, + GET_CBLK +}; + +class BpEffect: public BpInterface<IEffect> +{ +public: + BpEffect(const sp<IBinder>& impl) + : BpInterface<IEffect>(impl) + { + } + + status_t enable() + { + LOGV("enable"); + Parcel data, reply; + data.writeInterfaceToken(IEffect::getInterfaceDescriptor()); + remote()->transact(ENABLE, data, &reply); + return reply.readInt32(); + } + + status_t disable() + { + LOGV("disable"); + Parcel data, reply; + data.writeInterfaceToken(IEffect::getInterfaceDescriptor()); + remote()->transact(DISABLE, data, &reply); + return reply.readInt32(); + } + + status_t command(int cmdCode, int cmdSize, void *pCmdData, int *pReplySize, void *pReplyData) + { + LOGV("command"); + Parcel data, reply; + data.writeInterfaceToken(IEffect::getInterfaceDescriptor()); + data.writeInt32(cmdCode); + int size = cmdSize; + if (pCmdData == NULL) { + size = 0; + } + data.writeInt32(size); + if (size) { + data.write(pCmdData, size); + } + if (pReplySize == NULL) { + size = 0; + } else { + size = *pReplySize; + } + data.writeInt32(size); + remote()->transact(COMMAND, data, &reply); + status_t status = reply.readInt32(); + size = reply.readInt32(); + if (size != 0 && pReplyData != NULL && pReplySize != NULL) { + reply.read(pReplyData, size); + *pReplySize = size; + } + return status; + } + + void disconnect() + { + LOGV("disconnect"); + Parcel data, reply; + data.writeInterfaceToken(IEffect::getInterfaceDescriptor()); + remote()->transact(DISCONNECT, data, &reply); + return; + } + + virtual sp<IMemory> getCblk() const + { + Parcel data, reply; + sp<IMemory> cblk; + data.writeInterfaceToken(IEffect::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_CBLK, data, &reply); + if (status == NO_ERROR) { + cblk = interface_cast<IMemory>(reply.readStrongBinder()); + } + return cblk; + } + }; + +IMPLEMENT_META_INTERFACE(Effect, "android.media.IEffect"); + +// ---------------------------------------------------------------------- + +status_t BnEffect::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case ENABLE: { + LOGV("ENABLE"); + CHECK_INTERFACE(IEffect, data, reply); + reply->writeInt32(enable()); + return NO_ERROR; + } break; + + case DISABLE: { + LOGV("DISABLE"); + CHECK_INTERFACE(IEffect, data, reply); + reply->writeInt32(disable()); + return NO_ERROR; + } break; + + case COMMAND: { + LOGV("COMMAND"); + CHECK_INTERFACE(IEffect, data, reply); + int cmdCode = data.readInt32(); + int cmdSize = data.readInt32(); + char *cmd = NULL; + if (cmdSize) { + cmd = (char *)malloc(cmdSize); + data.read(cmd, cmdSize); + } + int replySize = data.readInt32(); + int replySz = replySize; + char *resp = NULL; + if (replySize) { + resp = (char *)malloc(replySize); + } + status_t status = command(cmdCode, cmdSize, cmd, &replySz, resp); + reply->writeInt32(status); + if (replySz < replySize) { + replySize = replySz; + } + reply->writeInt32(replySize); + if (replySize) { + reply->write(resp, replySize); + } + if (cmd) { + free(cmd); + } + if (resp) { + free(resp); + } + return NO_ERROR; + } break; + + case DISCONNECT: { + LOGV("DISCONNECT"); + CHECK_INTERFACE(IEffect, data, reply); + disconnect(); + return NO_ERROR; + } break; + + case GET_CBLK: { + CHECK_INTERFACE(IEffect, data, reply); + reply->writeStrongBinder(getCblk()->asBinder()); + return NO_ERROR; + } break; + + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/media/libmedia/IEffectClient.cpp b/media/libmedia/IEffectClient.cpp new file mode 100644 index 0000000..e7659ae --- /dev/null +++ b/media/libmedia/IEffectClient.cpp @@ -0,0 +1,141 @@ +/* +** +** Copyright 2010, 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_NDEBUG 0 +#define LOG_TAG "IEffectClient" +#include <utils/Log.h> +#include <stdint.h> +#include <sys/types.h> +#include <media/IEffectClient.h> + +namespace android { + +enum { + CONTROL_STATUS_CHANGED = IBinder::FIRST_CALL_TRANSACTION, + ENABLE_STATUS_CHANGED, + COMMAND_EXECUTED +}; + +class BpEffectClient: public BpInterface<IEffectClient> +{ +public: + BpEffectClient(const sp<IBinder>& impl) + : BpInterface<IEffectClient>(impl) + { + } + + void controlStatusChanged(bool controlGranted) + { + LOGV("controlStatusChanged"); + Parcel data, reply; + data.writeInterfaceToken(IEffectClient::getInterfaceDescriptor()); + data.writeInt32((uint32_t)controlGranted); + remote()->transact(CONTROL_STATUS_CHANGED, data, &reply, IBinder::FLAG_ONEWAY); + } + + void enableStatusChanged(bool enabled) + { + LOGV("enableStatusChanged"); + Parcel data, reply; + data.writeInterfaceToken(IEffectClient::getInterfaceDescriptor()); + data.writeInt32((uint32_t)enabled); + remote()->transact(ENABLE_STATUS_CHANGED, data, &reply, IBinder::FLAG_ONEWAY); + } + + void commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData) + { + LOGV("commandExecuted"); + Parcel data, reply; + data.writeInterfaceToken(IEffectClient::getInterfaceDescriptor()); + data.writeInt32(cmdCode); + int size = cmdSize; + if (pCmdData == NULL) { + size = 0; + } + data.writeInt32(size); + if (size) { + data.write(pCmdData, size); + } + size = replySize; + if (pReplyData == NULL) { + size = 0; + } + data.writeInt32(size); + if (size) { + data.write(pReplyData, size); + } + remote()->transact(COMMAND_EXECUTED, data, &reply, IBinder::FLAG_ONEWAY); + } + +}; + +IMPLEMENT_META_INTERFACE(EffectClient, "android.media.IEffectClient"); + +// ---------------------------------------------------------------------- + +status_t BnEffectClient::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case CONTROL_STATUS_CHANGED: { + LOGV("CONTROL_STATUS_CHANGED"); + CHECK_INTERFACE(IEffectClient, data, reply); + bool hasControl = (bool)data.readInt32(); + controlStatusChanged(hasControl); + return NO_ERROR; + } break; + case ENABLE_STATUS_CHANGED: { + LOGV("ENABLE_STATUS_CHANGED"); + CHECK_INTERFACE(IEffectClient, data, reply); + bool enabled = (bool)data.readInt32(); + enableStatusChanged(enabled); + return NO_ERROR; + } break; + case COMMAND_EXECUTED: { + LOGV("COMMAND_EXECUTED"); + CHECK_INTERFACE(IEffectClient, data, reply); + int cmdCode = data.readInt32(); + int cmdSize = data.readInt32(); + char *cmd = NULL; + if (cmdSize) { + cmd = (char *)malloc(cmdSize); + data.read(cmd, cmdSize); + } + int replySize = data.readInt32(); + char *resp = NULL; + if (replySize) { + resp = (char *)malloc(replySize); + data.read(resp, replySize); + } + commandExecuted(cmdCode, cmdSize, cmd, replySize, resp); + if (cmd) { + free(cmd); + } + if (resp) { + free(resp); + } + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 681943f..8d15013 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -77,6 +77,8 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_mp3dec \ libstagefright_vorbisdec \ libstagefright_matroska \ + libstagefright_vpxdec \ + libvpx \ LOCAL_SHARED_LIBRARIES += \ libstagefright_amrnb_common \ diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index cd26e6b..87d7ebb 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -104,6 +104,7 @@ CameraSource::CameraSource(const sp<Camera> &camera) mNumFramesReceived(0), mNumFramesEncoded(0), mNumFramesDropped(0), + mBufferGroup(NULL), mStarted(false) { String8 s = mCamera->getParameters(); printf("params: \"%s\"\n", s.string()); @@ -118,6 +119,23 @@ CameraSource::~CameraSource() { } } +static int bytesPerPixelTimes10(const char *colorFormat) { + LOGI("color format: %s", colorFormat); + return 20; +#if 0 + // XXX: Fix Camera Hal bug? + // On sholes, it returns CameraParameters::PIXEL_FORMAT_YUV420SP??? + if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422SP) || + !strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422I) || + !strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_RGB565)) { + return 20; + } else if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420SP)) { + return 15; + } + CHECK_EQ(0, "Unknown color format"); +#endif +} + status_t CameraSource::start(MetaData *) { LOGV("start"); CHECK(!mStarted); @@ -126,6 +144,12 @@ status_t CameraSource::start(MetaData *) { CHECK_EQ(OK, mCamera->startRecording()); mStarted = true; + mBufferGroup = new MediaBufferGroup(); + String8 s = mCamera->getParameters(); + CameraParameters params(s); + const char *colorFormat = params.getPreviewFormat(); + const int size = (mWidth * mHeight * bytesPerPixelTimes10(colorFormat))/10; + mBufferGroup->add_buffer(new MediaBuffer(size)); return OK; } @@ -139,6 +163,8 @@ status_t CameraSource::stop() { mCamera->stopRecording(); releaseQueuedFrames(); + delete mBufferGroup; + mBufferGroup = NULL; LOGI("Frames received/encoded/dropped: %d/%d/%d, timestamp (us) last/first: %lld/%lld", mNumFramesReceived, mNumFramesEncoded, mNumFramesDropped, mLastFrameTimestampUs, mFirstFrameTimeUs); @@ -197,7 +223,7 @@ status_t CameraSource::read( ++mNumFramesEncoded; } - *buffer = new MediaBuffer(frame->size()); + mBufferGroup->acquire_buffer(buffer); memcpy((*buffer)->data(), frame->pointer(), frame->size()); (*buffer)->set_range(0, frame->size()); mCamera->releaseRecordingFrame(frame); diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 928a6c8..66011ca 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -28,6 +28,7 @@ #include "include/M4vH263Decoder.h" #include "include/MP3Decoder.h" #include "include/VorbisDecoder.h" +#include "include/VPXDecoder.h" #include "include/ESDS.h" @@ -76,6 +77,7 @@ FACTORY_CREATE(AACDecoder) FACTORY_CREATE(AVCDecoder) FACTORY_CREATE(M4vH263Decoder) FACTORY_CREATE(VorbisDecoder) +FACTORY_CREATE(VPXDecoder) FACTORY_CREATE_ENCODER(AMRNBEncoder) FACTORY_CREATE_ENCODER(AMRWBEncoder) FACTORY_CREATE_ENCODER(AACEncoder) @@ -118,6 +120,7 @@ static sp<MediaSource> InstantiateSoftwareCodec( FACTORY_REF(AVCDecoder) FACTORY_REF(M4vH263Decoder) FACTORY_REF(VorbisDecoder) + FACTORY_REF(VPXDecoder) }; for (size_t i = 0; i < sizeof(kFactoryInfo) / sizeof(kFactoryInfo[0]); ++i) { @@ -158,6 +161,7 @@ static const CodecInfo kDecoderInfo[] = { { MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" }, // { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcdec" }, { MEDIA_MIMETYPE_AUDIO_VORBIS, "VorbisDecoder" }, + { MEDIA_MIMETYPE_VIDEO_VPX, "VPXDecoder" }, }; static const CodecInfo kEncoderInfo[] = { diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp index bd16db9..6013b6f 100644 --- a/media/libstagefright/OggExtractor.cpp +++ b/media/libstagefright/OggExtractor.cpp @@ -76,7 +76,7 @@ struct MyVorbisExtractor { status_t seekToOffset(off_t offset); status_t readNextPacket(MediaBuffer **buffer); - void init(); + status_t init(); sp<MetaData> getFileMetaData() { return mFileMeta; } @@ -107,7 +107,7 @@ private: ssize_t readPage(off_t offset, Page *page); status_t findNextPage(off_t startOffset, off_t *pageOffset); - void verifyHeader( + status_t verifyHeader( MediaBuffer *buffer, uint8_t type); void parseFileMetaData(); @@ -308,6 +308,7 @@ ssize_t MyVorbisExtractor::readPage(off_t offset, Page *page) { totalSize += page->mLace[i]; } +#if 0 String8 tmp; for (size_t i = 0; i < page->mNumSegments; ++i) { char x[32]; @@ -316,7 +317,8 @@ ssize_t MyVorbisExtractor::readPage(off_t offset, Page *page) { tmp.append(x); } - // LOGV("%c %s", page->mFlags & 1 ? '+' : ' ', tmp.string()); + LOGV("%c %s", page->mFlags & 1 ? '+' : ' ', tmp.string()); +#endif return sizeof(header) + page->mNumSegments + totalSize; } @@ -432,43 +434,60 @@ status_t MyVorbisExtractor::readNextPacket(MediaBuffer **out) { } } -void MyVorbisExtractor::init() { +status_t MyVorbisExtractor::init() { mMeta = new MetaData; mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS); MediaBuffer *packet; - CHECK_EQ(readNextPacket(&packet), OK); + status_t err; + if ((err = readNextPacket(&packet)) != OK) { + return err; + } LOGV("read packet of size %d\n", packet->range_length()); - verifyHeader(packet, 1); + err = verifyHeader(packet, 1); packet->release(); packet = NULL; + if (err != OK) { + return err; + } - CHECK_EQ(readNextPacket(&packet), OK); + if ((err = readNextPacket(&packet)) != OK) { + return err; + } LOGV("read packet of size %d\n", packet->range_length()); - verifyHeader(packet, 3); + err = verifyHeader(packet, 3); packet->release(); packet = NULL; + if (err != OK) { + return err; + } - CHECK_EQ(readNextPacket(&packet), OK); + if ((err = readNextPacket(&packet)) != OK) { + return err; + } LOGV("read packet of size %d\n", packet->range_length()); - verifyHeader(packet, 5); + err = verifyHeader(packet, 5); packet->release(); packet = NULL; + if (err != OK) { + return err; + } mFirstDataOffset = mOffset + mCurrentPageSize; + + return OK; } -void MyVorbisExtractor::verifyHeader( +status_t MyVorbisExtractor::verifyHeader( MediaBuffer *buffer, uint8_t type) { const uint8_t *data = (const uint8_t *)buffer->data() + buffer->range_offset(); size_t size = buffer->range_length(); - CHECK(size >= 7); - - CHECK_EQ(data[0], type); - CHECK(!memcmp(&data[1], "vorbis", 6)); + if (size < 7 || data[0] != type || memcmp(&data[1], "vorbis", 6)) { + return ERROR_MALFORMED; + } ogg_buffer buf; buf.data = (uint8_t *)data; @@ -515,7 +534,9 @@ void MyVorbisExtractor::verifyHeader( case 3: { - CHECK_EQ(0, _vorbis_unpack_comment(&mVc, &bits)); + if (0 != _vorbis_unpack_comment(&mVc, &bits)) { + return ERROR_MALFORMED; + } parseFileMetaData(); break; @@ -523,12 +544,16 @@ void MyVorbisExtractor::verifyHeader( case 5: { - CHECK_EQ(0, _vorbis_unpack_books(&mVi, &bits)); + if (0 != _vorbis_unpack_books(&mVi, &bits)) { + return ERROR_MALFORMED; + } mMeta->setData(kKeyVorbisBooks, 0, data, size); break; } } + + return OK; } uint64_t MyVorbisExtractor::approxBitrate() { @@ -732,10 +757,11 @@ OggExtractor::OggExtractor(const sp<DataSource> &source) mInitCheck(NO_INIT), mImpl(NULL) { mImpl = new MyVorbisExtractor(mDataSource); - CHECK_EQ(mImpl->seekToOffset(0), OK); - mImpl->init(); + mInitCheck = mImpl->seekToOffset(0); - mInitCheck = OK; + if (mInitCheck == OK) { + mInitCheck = mImpl->init(); + } } OggExtractor::~OggExtractor() { diff --git a/media/libstagefright/codecs/on2/Android.mk b/media/libstagefright/codecs/on2/Android.mk new file mode 100644 index 0000000..2e43120 --- /dev/null +++ b/media/libstagefright/codecs/on2/Android.mk @@ -0,0 +1,4 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/media/libstagefright/codecs/on2/dec/Android.mk b/media/libstagefright/codecs/on2/dec/Android.mk new file mode 100644 index 0000000..03dfb75 --- /dev/null +++ b/media/libstagefright/codecs/on2/dec/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + VPXDecoder.cpp + +LOCAL_MODULE := libstagefright_vpxdec + +LOCAL_C_INCLUDES := \ + $(TOP)/frameworks/base/media/libstagefright/include \ + $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \ + $(TOP)/external/libvpx \ + $(TOP)/external/libvpx/vpx_codec \ + $(TOP)/external/libvpx/vpx_ports + +include $(BUILD_STATIC_LIBRARY) diff --git a/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp b/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp new file mode 100644 index 0000000..bad8956 --- /dev/null +++ b/media/libstagefright/codecs/on2/dec/VPXDecoder.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2010 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_NDEBUG 0 +#define LOG_TAG "VPXDecoder" +#include <utils/Log.h> + +#include "VPXDecoder.h" + +#include <OMX_Component.h> + +#include <media/stagefright/MediaBufferGroup.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/Utils.h> + +#include "vpx_codec/vpx_decoder.h" +#include "vp8/vp8dx.h" + +namespace android { + +VPXDecoder::VPXDecoder(const sp<MediaSource> &source) + : mSource(source), + mStarted(false), + mBufferSize(0), + mCtx(NULL), + mBufferGroup(NULL) { + sp<MetaData> inputFormat = source->getFormat(); + const char *mime; + CHECK(inputFormat->findCString(kKeyMIMEType, &mime)); + CHECK(!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_VPX)); + + CHECK(inputFormat->findInt32(kKeyWidth, &mWidth)); + CHECK(inputFormat->findInt32(kKeyHeight, &mHeight)); + + mBufferSize = (mWidth * mHeight * 3) / 2; + + mFormat = new MetaData; + mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); + mFormat->setInt32(kKeyWidth, mWidth); + mFormat->setInt32(kKeyHeight, mHeight); + mFormat->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar); + mFormat->setCString(kKeyDecoderComponent, "VPXDecoder"); + + int64_t durationUs; + if (inputFormat->findInt64(kKeyDuration, &durationUs)) { + mFormat->setInt64(kKeyDuration, durationUs); + } +} + +VPXDecoder::~VPXDecoder() { + if (mStarted) { + stop(); + } +} + +status_t VPXDecoder::start(MetaData *) { + if (mStarted) { + return UNKNOWN_ERROR; + } + + status_t err = mSource->start(); + + if (err != OK) { + return err; + } + + mCtx = new vpx_codec_ctx_t; + if (vpx_codec_dec_init( + (vpx_codec_ctx_t *)mCtx, &vpx_codec_vp8_dx_algo, NULL, 0)) { + LOGE("on2 decoder failed to initialize."); + + mSource->stop(); + + return UNKNOWN_ERROR; + } + + mBufferGroup = new MediaBufferGroup; + mBufferGroup->add_buffer(new MediaBuffer(mBufferSize)); + mBufferGroup->add_buffer(new MediaBuffer(mBufferSize)); + + mStarted = true; + + return OK; +} + +status_t VPXDecoder::stop() { + if (!mStarted) { + return UNKNOWN_ERROR; + } + + delete mBufferGroup; + mBufferGroup = NULL; + + vpx_codec_destroy((vpx_codec_ctx_t *)mCtx); + delete (vpx_codec_ctx_t *)mCtx; + mCtx = NULL; + + mSource->stop(); + + mStarted = false; + + return OK; +} + +sp<MetaData> VPXDecoder::getFormat() { + return mFormat; +} + +status_t VPXDecoder::read( + MediaBuffer **out, const ReadOptions *options) { + *out = NULL; + + MediaBuffer *input; + status_t err = mSource->read(&input, options); + + if (err != OK) { + return err; + } + + LOGV("read %d bytes from source\n", input->range_length()); + + if (vpx_codec_decode( + (vpx_codec_ctx_t *)mCtx, + (uint8_t *)input->data() + input->range_offset(), + input->range_length(), + NULL, + 0)) { + LOGE("on2 decoder failed to decode frame."); + input->release(); + input = NULL; + + return UNKNOWN_ERROR; + } + + LOGV("successfully decoded 1 or more frames."); + + int64_t timeUs; + CHECK(input->meta_data()->findInt64(kKeyTime, &timeUs)); + + input->release(); + input = NULL; + + vpx_codec_iter_t iter = NULL; + vpx_image_t *img = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter); + + if (img == NULL) { + LOGI("on2 decoder did not return a frame."); + + *out = new MediaBuffer(0); + return OK; + } + + CHECK_EQ(img->fmt, IMG_FMT_I420); + + int32_t width = img->d_w; + int32_t height = img->d_h; + + if (width != mWidth || height != mHeight) { + LOGI("Image dimensions changed, width = %d, height = %d", + width, height); + + mWidth = width; + mHeight = height; + mFormat->setInt32(kKeyWidth, width); + mFormat->setInt32(kKeyHeight, height); + + mBufferSize = (mWidth * mHeight * 3) / 2; + delete mBufferGroup; + mBufferGroup = new MediaBufferGroup; + mBufferGroup->add_buffer(new MediaBuffer(mBufferSize)); + mBufferGroup->add_buffer(new MediaBuffer(mBufferSize)); + + return INFO_FORMAT_CHANGED; + } + + MediaBuffer *output; + CHECK_EQ(mBufferGroup->acquire_buffer(&output), OK); + + const uint8_t *srcLine = (const uint8_t *)img->planes[PLANE_Y]; + uint8_t *dst = (uint8_t *)output->data(); + for (size_t i = 0; i < img->d_h; ++i) { + memcpy(dst, srcLine, img->d_w); + + srcLine += img->stride[PLANE_Y]; + dst += img->d_w; + } + + srcLine = (const uint8_t *)img->planes[PLANE_U]; + for (size_t i = 0; i < img->d_h / 2; ++i) { + memcpy(dst, srcLine, img->d_w / 2); + + srcLine += img->stride[PLANE_U]; + dst += img->d_w / 2; + } + + srcLine = (const uint8_t *)img->planes[PLANE_V]; + for (size_t i = 0; i < img->d_h / 2; ++i) { + memcpy(dst, srcLine, img->d_w / 2); + + srcLine += img->stride[PLANE_V]; + dst += img->d_w / 2; + } + + output->set_range(0, (width * height * 3) / 2); + + output->meta_data()->setInt64(kKeyTime, timeUs); + + *out = output; + + return OK; +} + +} // namespace android + diff --git a/media/libstagefright/include/VPXDecoder.h b/media/libstagefright/include/VPXDecoder.h new file mode 100644 index 0000000..550c612 --- /dev/null +++ b/media/libstagefright/include/VPXDecoder.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 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 VPX_DECODER_H_ + +#define VPX_DECODER_H_ + +#include <media/stagefright/MediaSource.h> +#include <utils/Vector.h> + +namespace android { + +struct MediaBufferGroup; + +struct VPXDecoder : public MediaSource { + VPXDecoder(const sp<MediaSource> &source); + + virtual status_t start(MetaData *params); + virtual status_t stop(); + + virtual sp<MetaData> getFormat(); + + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options); + +protected: + virtual ~VPXDecoder(); + +private: + sp<MediaSource> mSource; + bool mStarted; + int32_t mWidth, mHeight; + size_t mBufferSize; + + void *mCtx; + MediaBufferGroup *mBufferGroup; + + sp<MetaData> mFormat; + + VPXDecoder(const VPXDecoder &); + VPXDecoder &operator=(const VPXDecoder &); +}; + +} // namespace android + +#endif // VPX_DECODER_H_ + |