Enable VAVideoDecoder as thumbnail generation path (Video HAL)
BZ: 48580
Use Intel HW decoder as the thumbnail generation path to improve
performance.
Change-Id: Ifd6e966cdea40e8cc038963b31ab64ed4bd74551
Signed-off-by: wfeng6 <wei.feng@intel.com>
Reviewed-on: http://android.intel.com:8080/58656
Reviewed-by: buildbot <buildbot@intel.com>
Reviewed-by: Zhang, Xiaolin <xiaolin.zhang@intel.com>
Tested-by: Zhang, Xiaolin <xiaolin.zhang@intel.com>
diff --git a/Android.mk b/Android.mk
index b73ddce..1056c24 100644
--- a/Android.mk
+++ b/Android.mk
@@ -13,4 +13,5 @@
include $(VENDORS_INTEL_MRST_LIBMIX_ROOT)/videodecoder/Android.mk
include $(VENDORS_INTEL_MRST_LIBMIX_ROOT)/videoencoder/Android.mk
include $(VENDORS_INTEL_MRST_LIBMIX_ROOT)/frameworks/asf_extractor/Android.mk
+include $(VENDORS_INTEL_MRST_LIBMIX_ROOT)/frameworks/vavideodecoder/Android.mk
endif
diff --git a/frameworks/vavideodecoder/Android.mk b/frameworks/vavideodecoder/Android.mk
new file mode 100644
index 0000000..1a9faaf
--- /dev/null
+++ b/frameworks/vavideodecoder/Android.mk
@@ -0,0 +1,27 @@
+ifeq ($(strip $(USE_INTEL_ASF_EXTRACTOR)),true)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_COPY_HEADERS_TO := libvavideodecoder
+LOCAL_COPY_HEADERS := VAVideoDecoder.h
+
+LOCAL_SRC_FILES := \
+ VAVideoDecoder.cpp \
+
+#LOCAL_SHARED_LIBRARIES += libasfparser
+
+LOCAL_C_INCLUDES := \
+ $(TOP)/frameworks/av/media/libstagefright/include \
+ $(TOP)/frameworks/native/include/media/openmax \
+ $(TARGET_OUT_HEADERS)/libmix_videodecoder \
+ $(TARGET_OUT_HEADERS)/libmix_asf_extractor \
+ $(TARGET_OUT_HEADERS)/libva/
+
+
+LOCAL_MODULE:= libvavideodecoder
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
+
+endif
diff --git a/frameworks/vavideodecoder/VAVideoDecoder.cpp b/frameworks/vavideodecoder/VAVideoDecoder.cpp
new file mode 100644
index 0000000..a860e86
--- /dev/null
+++ b/frameworks/vavideodecoder/VAVideoDecoder.cpp
@@ -0,0 +1,340 @@
+/*
+* Copyright (c) 2009-2011 Intel Corporation. All rights reserved.
+*
+* 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 "VAVideoDecoder"
+#include <utils/Log.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 <media/openmax/OMX_IVCommon.h>
+#include "MetaDataExt.h"
+#include "VAVideoDecoder.h"
+#include "VideoDecoderInterface.h"
+#include "VideoDecoderHost.h"
+#include <media/stagefright/foundation/ADebug.h>
+namespace android {
+
+VAVideoDecoder::VAVideoDecoder(const sp<MediaSource> &source)
+ : mSource(source),
+ mStarted(false),
+ mRawOutput(false),
+ mInputBuffer(NULL),
+ mTargetTimeUs(-1),
+ mFrameIndex(0),
+ mErrorCount(0),
+ mDecoder(NULL) {
+
+ const char *mime;
+ CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));
+ mDecoder = createVideoDecoder(mime);
+ if (mDecoder == NULL) {
+ LOGE("Failed to create video decoder for %s", mime);
+ }
+
+ mFormat = new MetaData;
+ mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VA);
+
+ int32_t width, height;
+ CHECK(mSource->getFormat()->findInt32(kKeyWidth, &width));
+ CHECK(mSource->getFormat()->findInt32(kKeyHeight, &height));
+ mFormat->setInt32(kKeyWidth, width);
+ mFormat->setInt32(kKeyHeight, height);
+ mFormat->setInt32(kKeyColorFormat, OMX_INTEL_COLOR_FormatYUV420PackedSemiPlanar);
+ mFormat->setCString(kKeyDecoderComponent, "VAVideoDecoder");
+
+ int64_t durationUs;
+ if (mSource->getFormat()->findInt64(kKeyDuration, &durationUs)) {
+ mFormat->setInt64(kKeyDuration, durationUs);
+ }
+
+}
+
+VAVideoDecoder::~VAVideoDecoder() {
+ if (mStarted) {
+ stop();
+ }
+ releaseVideoDecoder(mDecoder);
+ mDecoder = NULL;
+}
+
+status_t VAVideoDecoder::start(MetaData *params) {
+ CHECK(!mStarted);
+
+ if (mDecoder == NULL) {
+ LOGE("Decoder is not created.");
+ return UNKNOWN_ERROR;
+ }
+
+ int32_t ret;
+ char str[255];
+ sprintf(str, "%d", gettid());
+ ret = setenv("PSB_VIDEO_THUMBNAIL", str, 1);
+ if (ret) {
+ LOGW("Set environmnet'PSB_VIDEO_SURFACE_MMU' fail\n");
+ }
+ uint32_t type;
+ const void *data;
+ size_t size;
+ sp<MetaData> meta = mSource->getFormat();
+ VideoConfigBuffer configBuffer;
+ memset(&configBuffer, 0, sizeof(VideoConfigBuffer));
+
+ if (meta->findData(kKeyConfigData, &type, &data, &size) ||
+ meta->findData(kKeyESDS, &type, &data, &size) ||
+ meta->findData(kKeyAVCC, &type, &data, &size)) {
+ configBuffer.data = (uint8_t*)data;
+ configBuffer.size = size;
+ } else {
+ LOGW("No configuration data found!");
+ }
+
+ configBuffer.flag |= WANT_RAW_OUTPUT;
+ mFormat->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420SemiPlanar);
+ mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
+ mRawOutput = true;
+ LOGW("Decoder will output raw data.");
+
+ mFormat->findInt32(kKeyWidth, &configBuffer.width);
+ mFormat->findInt32(kKeyHeight, &configBuffer.height);
+
+ Decode_Status res = mDecoder->start(&configBuffer);
+ if (res != DECODE_SUCCESS) {
+ LOGE("Failed to start decoder. Error = %d", res);
+ return UNKNOWN_ERROR;
+ }
+
+ // TODO: update format meta, including frame cropping information.
+
+ // create MediaBuffer pool only when output is VASurface
+ if (mRawOutput == false) {
+ for (int32_t i = 0; i < NUM_OF_MEDIA_BUFFER; ++i) {
+ MediaBuffer *buffer = new MediaBuffer(sizeof(VideoRenderBuffer));
+ buffer->setObserver(this);
+ // output is unreadable VASurface
+ buffer->meta_data()->setInt32(kKeyIsUnreadable, 1);
+ buffer->meta_data()->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VA);
+ buffer->meta_data()->setInt32(kKeyColorFormat,OMX_COLOR_FormatYUV420SemiPlanar);
+ mFrames.push(buffer);
+ }
+ }
+
+ mSource->start();
+ unsetenv("PSB_VIDEO_THUMBNAIL");
+
+ mFrameIndex = 0;
+ mErrorCount = 0;
+ mTargetTimeUs = -1;
+ mStarted = true;
+ return OK;
+}
+
+
+status_t VAVideoDecoder::stop() {
+ CHECK(mStarted);
+
+ if (mInputBuffer) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ for (size_t i = 0; i < mFrames.size(); ++i) {
+ MediaBuffer *buffer = mFrames.editItemAt(i);
+ buffer->setObserver(NULL);
+ buffer->release();
+ }
+ mFrames.clear();
+ mSource->stop();
+ mDecoder->stop();
+
+ mFrameIndex = 0;
+ mErrorCount = 0;
+ mRawOutput = false;
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> VAVideoDecoder::getFormat() {
+ return mFormat;
+}
+
+MediaBuffer *VAVideoDecoder::getOutputBuffer(bool bDraining) {
+ const VideoRenderBuffer* buffer = mDecoder->getOutput(bDraining);
+ if (buffer == NULL) {
+ return NULL;
+ }
+ // indicate buffer is rendered
+ buffer->renderDone = true;
+
+ if (mTargetTimeUs >= 0) {
+ CHECK(buffer->timeStamp <= mTargetTimeUs);
+ if (buffer->timeStamp < mTargetTimeUs) {
+ // We're still waiting for the frame with the matching
+ // timestamp and we won't return the current one.
+ LOGV("skipping frame at %lld us", buffer->timeStamp);
+ return NULL;
+ } else {
+ LOGV("found target frame at %lld us", buffer->timeStamp);
+ mTargetTimeUs = -1;
+ }
+ }
+
+ MediaBuffer *mbuf = NULL;
+ if (mRawOutput == false) {
+ mbuf = mFrames.editItemAt(mFrameIndex);
+ mFrameIndex++;
+ if (mFrameIndex >= mFrames.size()) {
+ mFrameIndex = 0;
+ }
+ memcpy(mbuf->data(), buffer, sizeof(VideoRenderBuffer));
+ mbuf->meta_data()->setInt64(kKeyTime, buffer->timeStamp);
+ mbuf->set_range(0, mbuf->size());
+ mbuf->add_ref();
+ } else {
+ mbuf = new MediaBuffer(buffer->rawData->data, buffer->rawData->size);
+ mbuf->meta_data()->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
+ mbuf->meta_data()->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420SemiPlanar);
+ mbuf->meta_data()->setInt64(kKeyTime, buffer->timeStamp);
+ }
+
+ return mbuf;
+}
+
+status_t VAVideoDecoder::read(MediaBuffer **out, const ReadOptions *options) {
+ *out = NULL;
+ if (mDecoder == NULL) {
+ LOGE("Decoder is not created.");
+ return UNKNOWN_ERROR;
+ }
+
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode mode;
+ ReadOptions seekOptions;
+ bool seeking = false;
+
+ if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+ LOGV("seek requested to %lld us (%.2f secs)", seekTimeUs, seekTimeUs / 1E6);
+
+ if (seekTimeUs < 0) {
+ LOGE("Invalid seek time : %ld", (long int32_t)seekTimeUs);
+ seekTimeUs = 0;
+ //return ERROR_END_OF_STREAM;
+ }
+ //CHECK(seekTimeUs >= 0);
+
+ seekOptions.setSeekTo(seekTimeUs, mode);
+ mDecoder->flush();
+ seeking = true;
+ }
+
+ for (;;) {
+ status_t err = mSource->read(&mInputBuffer, &seekOptions);
+ seekOptions.clearSeekTo();
+
+ if (err != OK) {
+ LOGE("Failed to read buffer from source extractor.");
+ // drain the output buffer when end of stream
+ *out = getOutputBuffer(true);
+ return (*out == NULL) ? err : (status_t)OK;
+ }
+
+ if (mInputBuffer->range_length() > 0) {
+ break;
+ }
+
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ if (mInputBuffer == NULL) {
+ LOGE("Unexpected NULL input buffer.");
+ return ERROR_END_OF_STREAM;
+ }
+
+ if (seeking) {
+ int64_t targetTimeUs;
+ if (mInputBuffer->meta_data()->findInt64(kKeyTargetTime, &targetTimeUs) && targetTimeUs >= 0) {
+ mTargetTimeUs = targetTimeUs;
+ } else {
+ mTargetTimeUs = -1;
+ }
+ }
+
+ status_t err = UNKNOWN_ERROR;
+
+ // prepare decoding buffer
+ VideoDecodeBuffer decodeBuffer;
+ memset(&decodeBuffer, 0, sizeof(decodeBuffer));
+ decodeBuffer.data = (uint8_t*)mInputBuffer->data() + mInputBuffer->range_offset();
+ decodeBuffer.size = mInputBuffer->range_length();
+ decodeBuffer.flag = seeking ? HAS_DISCONTINUITY : 0;
+ mInputBuffer->meta_data()->findInt64(kKeyTime, &decodeBuffer.timeStamp);
+ Decode_Status res = mDecoder->decode(&decodeBuffer);
+
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+
+ if (res == DECODE_FORMAT_CHANGE) {
+ LOGW("Format changed.");
+ // drain all the frames.
+ MediaBuffer *mbuf = NULL;
+ while ((mbuf = getOutputBuffer(true)) != NULL) {
+ mbuf->release();
+ }
+ const VideoFormatInfo *info = mDecoder->getFormatInfo();
+ uint32_t cropWidth, cropHeight;
+ if (info != NULL) {
+ cropWidth = info->width - (info->cropLeft + info->cropRight);
+ cropHeight = info->height - (info->cropBottom + info->cropTop);
+ mFormat->setInt32(kKeyWidth, cropWidth);
+ mFormat->setInt32(kKeyHeight, cropHeight);
+ }
+ // TODO: handle format change
+ err = INFO_FORMAT_CHANGED;
+ }
+ else if (res == DECODE_SUCCESS) {
+ mErrorCount = 0;
+ err = OK;
+ MediaBuffer *mbuf = getOutputBuffer();
+ if (mbuf == NULL) {
+ *out = new MediaBuffer(0);
+ } else {
+ *out = mbuf;
+ }
+ } else {
+ mErrorCount++;
+ LOGE("Failed to decode buffer (#%d). Error = %d", mErrorCount, res);
+ if (checkFatalDecoderError(res)) {
+ err = UNKNOWN_ERROR;
+ } else {
+ // For decoder errors that could be omitted, not throw error and continue to decode.
+ err = OK;
+ *out = new MediaBuffer(0);
+ }
+ }
+
+ return err;
+}
+
+}// namespace android
+
diff --git a/frameworks/vavideodecoder/VAVideoDecoder.h b/frameworks/vavideodecoder/VAVideoDecoder.h
new file mode 100644
index 0000000..911b0dc
--- /dev/null
+++ b/frameworks/vavideodecoder/VAVideoDecoder.h
@@ -0,0 +1,65 @@
+/*
+* Copyright (c) 2009-2011 Intel Corporation. All rights reserved.
+*
+* 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 VA_VIDEO_DECODER_H_
+#define VA_VIDEO_DECODER_H_
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/Vector.h>
+
+class IVideoDecoder;
+
+namespace android {
+
+struct VAVideoDecoder : public MediaSource,
+ public MediaBufferObserver {
+ VAVideoDecoder(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);
+ virtual void signalBufferReturned(MediaBuffer* buffer) {}
+
+protected:
+ virtual ~VAVideoDecoder();
+
+private:
+ MediaBuffer *getOutputBuffer(bool bDraining = false);
+ VAVideoDecoder(const VAVideoDecoder &);
+ VAVideoDecoder &operator=(const VAVideoDecoder &);
+
+private:
+ enum {
+ NUM_OF_MEDIA_BUFFER = 20,
+ };
+ sp<MediaSource> mSource;
+ bool mStarted;
+ bool mRawOutput;
+ sp<MetaData> mFormat;
+ Vector<MediaBuffer *> mFrames;
+ MediaBuffer *mInputBuffer;
+ int64_t mTargetTimeUs;
+ uint32_t mFrameIndex;
+ uint32_t mErrorCount;
+ IVideoDecoder *mDecoder;
+};
+
+} // namespace android
+
+#endif // VA_VIDEO_DECODER_H_