summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
Diffstat (limited to 'media')
-rw-r--r--media/libmedia/Android.mk4
-rw-r--r--media/libmedia/IEffect.cpp191
-rw-r--r--media/libmedia/IEffectClient.cpp141
-rw-r--r--media/libstagefright/Android.mk2
-rw-r--r--media/libstagefright/CameraSource.cpp28
-rw-r--r--media/libstagefright/OMXCodec.cpp4
-rw-r--r--media/libstagefright/OggExtractor.cpp66
-rw-r--r--media/libstagefright/codecs/on2/Android.mk4
-rw-r--r--media/libstagefright/codecs/on2/dec/Android.mk16
-rw-r--r--media/libstagefright/codecs/on2/dec/VPXDecoder.cpp230
-rw-r--r--media/libstagefright/include/VPXDecoder.h60
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_
+