Add YV12 color converter interface for VideoEditor.

The original assumption in VideoEditor is that the decoder output
and encoder input are in YV12 format. However on different
hardware platform the actual formats may be different. So now we
load a platform-specific YV12 color conversion module which
knows the actual format and can convert to/from YV12, which is
the format used in VideoEditor internally for processing.

Bug: 5061733
Change-Id: I852f85efd30c05cf6c42810059ee4d2ef37ee3da
diff --git a/libvideoeditor/lvpp/Android.mk b/libvideoeditor/lvpp/Android.mk
index be17b83..64e7c73 100755
--- a/libvideoeditor/lvpp/Android.mk
+++ b/libvideoeditor/lvpp/Android.mk
@@ -36,7 +36,8 @@
     VideoEditorBGAudioProcessing.cpp \
     AudioPlayerBase.cpp \
     PreviewPlayerBase.cpp \
-    PreviewRenderer.cpp
+    PreviewRenderer.cpp \
+    YV12ColorConverter.cpp
 
 LOCAL_MODULE_TAGS := optional
 
@@ -77,6 +78,7 @@
     $(TOP)/frameworks/media/libvideoeditor/vss/inc \
     $(TOP)/frameworks/media/libvideoeditor/vss/stagefrightshells/inc \
     $(TOP)/frameworks/media/libvideoeditor/lvpp \
+    $(TOP)/frameworks/media/libvideoeditor/include \
     $(TOP)/frameworks/base/media/jni/mediaeditor \
     $(TOP)/frameworks/base/services/audioflinger
 
diff --git a/libvideoeditor/lvpp/PreviewPlayer.cpp b/libvideoeditor/lvpp/PreviewPlayer.cpp
index b70ff4a..f2b82d9 100755
--- a/libvideoeditor/lvpp/PreviewPlayer.cpp
+++ b/libvideoeditor/lvpp/PreviewPlayer.cpp
@@ -178,7 +178,6 @@
 
     mVideoRenderer = NULL;
     mLastVideoBuffer = NULL;
-    mSuspensionState = NULL;
     mEffectsSettings = NULL;
     mVeAudioPlayer = NULL;
     mAudioMixStoryBoardTS = 0;
@@ -442,9 +441,6 @@
 
     mFileSource.clear();
 
-    delete mSuspensionState;
-    mSuspensionState = NULL;
-
     mCurrentVideoEffect = VIDEO_EFFECT_NONE;
     mIsVideoSourceJpg = false;
     mFrameRGBBuffer = NULL;
@@ -759,13 +755,8 @@
     if (mSurface != NULL) {
         sp<MetaData> meta = mVideoSource->getFormat();
 
-        int32_t format;
         const char *component;
-        int32_t decodedWidth, decodedHeight;
-        CHECK(meta->findInt32(kKeyColorFormat, &format));
         CHECK(meta->findCString(kKeyDecoderComponent, &component));
-        CHECK(meta->findInt32(kKeyWidth, &decodedWidth));
-        CHECK(meta->findInt32(kKeyHeight, &decodedHeight));
 
         // Must ensure that mVideoRenderer's destructor is actually executed
         // before creating a new one.
@@ -779,7 +770,7 @@
 
             mVideoRenderer = PreviewLocalRenderer:: initPreviewLocalRenderer (
                 false,  // previewOnly
-                (OMX_COLOR_FORMATTYPE)format,
+                OMX_COLOR_FormatYUV420Planar,
                 mSurface,
                 mOutputVideoWidth, mOutputVideoHeight,
                 mOutputVideoWidth, mOutputVideoHeight);
@@ -883,9 +874,7 @@
             }
         }
 
-        CHECK(mVideoTrack->getFormat()->findInt32(kKeyWidth, &mVideoWidth));
-        CHECK(mVideoTrack->getFormat()->findInt32(kKeyHeight, &mVideoHeight));
-
+        getVideoBufferSize(mVideoTrack->getFormat(), &mVideoWidth, &mVideoHeight);
         mReportedWidth = mVideoWidth;
         mReportedHeight = mVideoHeight;
 
@@ -958,7 +947,7 @@
                     mSeekTimeUs, MediaSource::ReadOptions::SEEK_CLOSEST);
         }
         for (;;) {
-            status_t err = mVideoSource->read(&mVideoBuffer, &options);
+            status_t err = readYV12Buffer(mVideoSource, &mVideoBuffer, &options);
             options.clearSeekTo();
 
             if (err != OK) {
@@ -968,9 +957,8 @@
                     LOGV("LV PLAYER VideoSource signalled format change");
                     notifyVideoSize_l();
                     sp<MetaData> meta = mVideoSource->getFormat();
+                    getVideoBufferSize(meta, &mReportedWidth, &mReportedHeight);
 
-                    CHECK(meta->findInt32(kKeyWidth, &mReportedWidth));
-                    CHECK(meta->findInt32(kKeyHeight, &mReportedHeight));
                     if (mVideoRenderer != NULL) {
                         mVideoRendererIsPreview = false;
                         err = initRenderer_l();
@@ -1418,77 +1406,6 @@
     mPreparedCondition.broadcast();
 }
 
-status_t PreviewPlayer::suspend() {
-    LOGV("suspend");
-    Mutex::Autolock autoLock(mLock);
-
-    if (mSuspensionState != NULL) {
-        if (mLastVideoBuffer == NULL) {
-            //go into here if video is suspended again
-            //after resuming without being played between
-            //them
-            SuspensionState *state = mSuspensionState;
-            mSuspensionState = NULL;
-            reset_l();
-            mSuspensionState = state;
-            return OK;
-        }
-
-        delete mSuspensionState;
-        mSuspensionState = NULL;
-    }
-
-    if (mFlags & PREPARING) {
-        mFlags |= PREPARE_CANCELLED;
-    }
-
-    while (mFlags & PREPARING) {
-        mPreparedCondition.wait(mLock);
-    }
-
-    SuspensionState *state = new SuspensionState;
-    state->mUri = mUri;
-    state->mUriHeaders = mUriHeaders;
-    state->mFileSource = mFileSource;
-
-    state->mFlags = mFlags & (PLAYING | AUTO_LOOPING | LOOPING | AT_EOS);
-    getPosition(&state->mPositionUs);
-
-    if (mLastVideoBuffer) {
-        size_t size = mLastVideoBuffer->range_length();
-        if (size) {
-            int32_t unreadable;
-            if (!mLastVideoBuffer->meta_data()->findInt32(
-                        kKeyIsUnreadable, &unreadable)
-                    || unreadable == 0) {
-                state->mLastVideoFrameSize = size;
-                state->mLastVideoFrame = malloc(size);
-                memcpy(state->mLastVideoFrame,
-                   (const uint8_t *)mLastVideoBuffer->data()
-                        + mLastVideoBuffer->range_offset(),
-                   size);
-
-                state->mVideoWidth = mVideoWidth;
-                state->mVideoHeight = mVideoHeight;
-
-                sp<MetaData> meta = mVideoSource->getFormat();
-                CHECK(meta->findInt32(kKeyColorFormat, &state->mColorFormat));
-                CHECK(meta->findInt32(kKeyWidth, &state->mDecodedWidth));
-                CHECK(meta->findInt32(kKeyHeight, &state->mDecodedHeight));
-            } else {
-                LOGV("Unable to save last video frame, we have no access to "
-                     "the decoded video data.");
-            }
-        }
-    }
-
-    reset_l();
-
-    mSuspensionState = state;
-
-    return OK;
-}
-
 void PreviewPlayer::acquireLock() {
     LOGV("acquireLock");
     mLockControl.lock();
@@ -1499,67 +1416,6 @@
     mLockControl.unlock();
 }
 
-status_t PreviewPlayer::resume() {
-    LOGV("resume");
-    Mutex::Autolock autoLock(mLock);
-
-    if (mSuspensionState == NULL) {
-        return INVALID_OPERATION;
-    }
-
-    SuspensionState *state = mSuspensionState;
-    mSuspensionState = NULL;
-
-    status_t err;
-    if (state->mFileSource != NULL) {
-        err = PreviewPlayerBase::setDataSource_l(state->mFileSource);
-
-        if (err == OK) {
-            mFileSource = state->mFileSource;
-        }
-    } else {
-        err = PreviewPlayerBase::setDataSource_l(state->mUri, &state->mUriHeaders);
-    }
-
-    if (err != OK) {
-        delete state;
-        state = NULL;
-
-        return err;
-    }
-
-    seekTo_l(state->mPositionUs);
-
-    mFlags = state->mFlags & (AUTO_LOOPING | LOOPING | AT_EOS);
-
-    if (state->mLastVideoFrame && (mSurface != NULL)) {
-        mVideoRenderer =
-            PreviewLocalRenderer::initPreviewLocalRenderer(
-                    true,  // previewOnly
-                    (OMX_COLOR_FORMATTYPE)state->mColorFormat,
-                    mSurface,
-                    state->mVideoWidth,
-                    state->mVideoHeight,
-                    state->mDecodedWidth,
-                    state->mDecodedHeight);
-
-        mVideoRendererIsPreview = true;
-
-        ((PreviewLocalRenderer *)mVideoRenderer.get())->render(
-                state->mLastVideoFrame, state->mLastVideoFrameSize);
-    }
-
-    if (state->mFlags & PLAYING) {
-        play_l();
-    }
-
-    mSuspensionState = state;
-    state = NULL;
-
-    return OK;
-}
-
-
 status_t PreviewPlayer::loadEffectsSettings(
                     M4VSS3GPP_EffectSettings* pEffectSettings, int nEffects) {
     M4OSA_UInt32 i = 0, rgbSize = 0;
@@ -1647,15 +1503,6 @@
     M4VIFI_UInt8 *tempOutputBuffer= M4OSA_NULL;
     size_t videoBufferSize = 0;
     M4OSA_UInt32 frameSize = 0, i=0, index =0, nFrameCount =0, bufferOffset =0;
-    int32_t colorFormat = 0;
-
-    if(!mIsVideoSourceJpg) {
-        sp<MetaData> meta = mVideoSource->getFormat();
-        CHECK(meta->findInt32(kKeyColorFormat, &colorFormat));
-    }
-    else {
-        colorFormat = OMX_COLOR_FormatYUV420Planar;
-    }
 
     videoBufferSize = mVideoBuffer->size();
     frameSize = (mVideoWidth*mVideoHeight*3) >> 1;
@@ -1841,22 +1688,6 @@
 M4OSA_ERR PreviewPlayer::doVideoPostProcessing() {
     M4OSA_ERR err = M4NO_ERROR;
     vePostProcessParams postProcessParams;
-    int32_t colorFormat = 0;
-
-
-    if(!mIsVideoSourceJpg) {
-        sp<MetaData> meta = mVideoSource->getFormat();
-        CHECK(meta->findInt32(kKeyColorFormat, &colorFormat));
-    }
-    else {
-        colorFormat = OMX_COLOR_FormatYUV420Planar;
-    }
-
-    if((colorFormat == OMX_COLOR_FormatYUV420SemiPlanar) ||
-       (colorFormat == 0x7FA30C00)) {
-          LOGE("doVideoPostProcessing: colorFormat YUV420Sp not supported");
-          return M4ERR_UNSUPPORTED_MEDIA_TYPE;
-    }
 
     postProcessParams.vidBuffer = (M4VIFI_UInt8*)mVideoBuffer->data()
         + mVideoBuffer->range_offset();
@@ -1900,7 +1731,7 @@
                     mSeekTimeUs, MediaSource::ReadOptions::SEEK_CLOSEST);
         }
         for (;;) {
-            status_t err = mVideoSource->read(&mVideoBuffer, &options);
+            status_t err = readYV12Buffer(mVideoSource, &mVideoBuffer, &options);
             options.clearSeekTo();
 
             if (err != OK) {
@@ -1910,9 +1741,7 @@
                     LOGV("LV PLAYER VideoSource signalled format change");
                     notifyVideoSize_l();
                     sp<MetaData> meta = mVideoSource->getFormat();
-
-                    CHECK(meta->findInt32(kKeyWidth, &mReportedWidth));
-                    CHECK(meta->findInt32(kKeyHeight, &mReportedHeight));
+                    getVideoBufferSize(meta, &mReportedWidth, &mReportedHeight);
 
                     if (mVideoRenderer != NULL) {
                         mVideoRendererIsPreview = false;
diff --git a/libvideoeditor/lvpp/PreviewPlayer.h b/libvideoeditor/lvpp/PreviewPlayer.h
index 74a09eb..ccd88a7 100755
--- a/libvideoeditor/lvpp/PreviewPlayer.h
+++ b/libvideoeditor/lvpp/PreviewPlayer.h
@@ -62,8 +62,6 @@
 
     status_t getVideoDimensions(int32_t *width, int32_t *height) const;
 
-    status_t suspend();
-    status_t resume();
     void acquireLock();
     void releaseLock();
 
@@ -134,32 +132,6 @@
 
     MediaBuffer *mLastVideoBuffer;
 
-    struct SuspensionState {
-        String8 mUri;
-        KeyedVector<String8, String8> mUriHeaders;
-        sp<DataSource> mFileSource;
-
-        uint32_t mFlags;
-        int64_t mPositionUs;
-
-        void *mLastVideoFrame;
-        size_t mLastVideoFrameSize;
-        int32_t mColorFormat;
-        int32_t mVideoWidth, mVideoHeight;
-        int32_t mDecodedWidth, mDecodedHeight;
-
-        SuspensionState()
-            : mLastVideoFrame(NULL) {
-        }
-
-        ~SuspensionState() {
-            if (mLastVideoFrame) {
-                free(mLastVideoFrame);
-                mLastVideoFrame = NULL;
-            }
-        }
-    } *mSuspensionState;
-
     //Data structures used for audio and video effects
     M4VSS3GPP_EffectSettings* mEffectsSettings;
     M4xVSS_AudioMixingSettings* mPreviewPlayerAudioMixSettings;
diff --git a/libvideoeditor/lvpp/PreviewPlayerBase.cpp b/libvideoeditor/lvpp/PreviewPlayerBase.cpp
index 62b8a72..8e27a27 100644
--- a/libvideoeditor/lvpp/PreviewPlayerBase.cpp
+++ b/libvideoeditor/lvpp/PreviewPlayerBase.cpp
@@ -204,6 +204,13 @@
 
     mAudioStatusEventPending = false;
 
+    mYV12ColorConverter = new YV12ColorConverter();
+    if (!mYV12ColorConverter->isLoaded() ||
+        mYV12ColorConverter->getDecoderOutputFormat() == OMX_COLOR_FormatYUV420Planar) {
+        delete mYV12ColorConverter;
+        mYV12ColorConverter = NULL;
+    }
+
     reset();
 }
 
@@ -212,6 +219,8 @@
         mQueue.stop();
     }
 
+    delete mYV12ColorConverter;
+
     reset();
 
     mClient.disconnect();
@@ -849,23 +858,33 @@
 void PreviewPlayerBase::notifyVideoSize_l() {
     sp<MetaData> meta = mVideoSource->getFormat();
 
+    int32_t vWidth, vHeight;
     int32_t cropLeft, cropTop, cropRight, cropBottom;
+
+    CHECK(meta->findInt32(kKeyWidth, &vWidth));
+    CHECK(meta->findInt32(kKeyHeight, &vHeight));
+
+    mGivenWidth = vWidth;
+    mGivenHeight = vHeight;
+
     if (!meta->findRect(
                 kKeyCropRect, &cropLeft, &cropTop, &cropRight, &cropBottom)) {
-        int32_t width, height;
-        CHECK(meta->findInt32(kKeyWidth, &width));
-        CHECK(meta->findInt32(kKeyHeight, &height));
 
         cropLeft = cropTop = 0;
-        cropRight = width - 1;
-        cropBottom = height - 1;
+        cropRight = vWidth - 1;
+        cropBottom = vHeight - 1;
 
-        LOGV("got dimensions only %d x %d", width, height);
+        LOGD("got dimensions only %d x %d", vWidth, vHeight);
     } else {
-        LOGV("got crop rect %d, %d, %d, %d",
+        LOGD("got crop rect %d, %d, %d, %d",
              cropLeft, cropTop, cropRight, cropBottom);
     }
 
+    mCropRect.left = cropLeft;
+    mCropRect.right = cropRight;
+    mCropRect.top = cropTop;
+    mCropRect.bottom = cropBottom;
+
     int32_t displayWidth;
     if (meta->findInt32(kKeyDisplayWidth, &displayWidth)) {
         LOGV("Display width changed (%d=>%d)", mDisplayWidth, displayWidth);
@@ -910,11 +929,8 @@
 
     int32_t format;
     const char *component;
-    int32_t decodedWidth, decodedHeight;
     CHECK(meta->findInt32(kKeyColorFormat, &format));
     CHECK(meta->findCString(kKeyDecoderComponent, &component));
-    CHECK(meta->findInt32(kKeyWidth, &decodedWidth));
-    CHECK(meta->findInt32(kKeyHeight, &decodedHeight));
 
     int32_t rotationDegrees;
     if (!mVideoTrack->getFormat()->findInt32(
@@ -1395,7 +1411,7 @@
                         : MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
         }
         for (;;) {
-            status_t err = mVideoSource->read(&mVideoBuffer, &options);
+            status_t err = readYV12Buffer(mVideoSource, &mVideoBuffer, &options);
             options.clearSeekTo();
 
             if (err != OK) {
@@ -1912,4 +1928,59 @@
 status_t PreviewPlayerBase::getParameter(int key, Parcel *reply) {
     return OK;
 }
+
+status_t PreviewPlayerBase::readYV12Buffer(sp<MediaSource> source, MediaBuffer **buffer,
+    const MediaSource::ReadOptions *options) {
+    status_t result = source->read(buffer, options);
+    if (mYV12ColorConverter == NULL || *buffer == NULL) {
+        return result;
+    }
+
+    int width = mCropRect.right - mCropRect.left + 1;
+    int height = mCropRect.bottom - mCropRect.top + 1;
+
+    MediaBuffer *origBuffer = *buffer;
+    MediaBuffer *newBuffer = new MediaBuffer(width * height * 3 / 2);
+
+    LOGD("convertDecoderOutputToYV12: mGivenWidth = %d, mGivenHeight = %d",
+        mGivenWidth, mGivenHeight);
+    LOGD("width = %d, height = %d", width, height);
+
+    if (mYV12ColorConverter->convertDecoderOutputToYV12(
+        (uint8_t *)origBuffer->data(), // ?? + origBuffer->range_offset(), // decoderBits
+        mGivenWidth,  // decoderWidth
+        mGivenHeight,  // decoderHeight
+        mCropRect, // decoderRect
+        (uint8_t *)newBuffer->data() + newBuffer->range_offset() /* dstBits */) < 0) {
+        LOGE("convertDecoderOutputToYV12 failed");
+    }
+
+    // Copy the timestamp
+    int64_t timeUs;
+    CHECK(origBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
+    newBuffer->meta_data()->setInt64(kKeyTime, timeUs);
+
+    origBuffer->release();
+    *buffer = newBuffer;
+
+    return result;
+}
+
+void PreviewPlayerBase::getVideoBufferSize(sp<MetaData> meta, int* width, int* height) {
+    if (mYV12ColorConverter) {
+        int32_t cropLeft, cropTop, cropRight, cropBottom;
+        if (meta->findRect(
+                kKeyCropRect, &cropLeft, &cropTop, &cropRight, &cropBottom)) {
+            *width = cropRight - cropLeft + 1;
+            *height = cropBottom - cropTop + 1;
+        } else {
+            CHECK(meta->findInt32(kKeyWidth, width));
+            CHECK(meta->findInt32(kKeyHeight, height));
+        }
+    } else {
+        CHECK(meta->findInt32(kKeyWidth, width));
+        CHECK(meta->findInt32(kKeyHeight, height));
+    }
+}
+
 }  // namespace android
diff --git a/libvideoeditor/lvpp/PreviewPlayerBase.h b/libvideoeditor/lvpp/PreviewPlayerBase.h
index a68d53c..c094e25 100644
--- a/libvideoeditor/lvpp/PreviewPlayerBase.h
+++ b/libvideoeditor/lvpp/PreviewPlayerBase.h
@@ -23,10 +23,12 @@
 
 #include <media/MediaPlayerInterface.h>
 #include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaSource.h>
 #include <media/stagefright/OMXClient.h>
 #include <media/stagefright/TimeSource.h>
 #include <utils/threads.h>
 #include <drm/DrmManagerClient.h>
+#include <YV12ColorConverter.h>
 
 namespace android {
 
@@ -99,6 +101,11 @@
     void postAudioEOS(int64_t delayUs = 0ll);
     void postAudioSeekComplete();
 
+protected:
+    status_t readYV12Buffer(sp<MediaSource> source, MediaBuffer **buffer,
+        const MediaSource::ReadOptions *options);
+    void getVideoBufferSize(sp<MetaData> meta, int* width, int* height);
+
 private:
     friend struct AwesomeEvent;
     friend struct PreviewPlayer;
@@ -223,6 +230,10 @@
 
     int64_t mLastVideoTimeUs;
 
+    ARect mCropRect;
+    int32_t mGivenWidth, mGivenHeight;
+    YV12ColorConverter *mYV12ColorConverter;
+
     status_t setDataSource_l(
             const char *uri,
             const KeyedVector<String8, String8> *headers = NULL);
diff --git a/libvideoeditor/lvpp/VideoEditorPlayer.cpp b/libvideoeditor/lvpp/VideoEditorPlayer.cpp
index 2af0e8c..1a298c6 100755
--- a/libvideoeditor/lvpp/VideoEditorPlayer.cpp
+++ b/libvideoeditor/lvpp/VideoEditorPlayer.cpp
@@ -173,16 +173,6 @@
     return STAGEFRIGHT_PLAYER;
 }
 
-status_t VideoEditorPlayer::suspend() {
-    LOGV("suspend");
-    return mPlayer->suspend();
-}
-
-status_t VideoEditorPlayer::resume() {
-    LOGV("resume");
-    return mPlayer->resume();
-}
-
 void VideoEditorPlayer::acquireLock() {
     LOGV("acquireLock");
     mPlayer->acquireLock();
diff --git a/libvideoeditor/lvpp/VideoEditorPlayer.h b/libvideoeditor/lvpp/VideoEditorPlayer.h
index 1afdd88..d1df0e0 100755
--- a/libvideoeditor/lvpp/VideoEditorPlayer.h
+++ b/libvideoeditor/lvpp/VideoEditorPlayer.h
@@ -112,8 +112,6 @@
     virtual player_type playerType();
     virtual status_t invoke(const Parcel &request, Parcel *reply);
     virtual void setAudioSink(const sp<AudioSink> &audioSink);
-    virtual status_t suspend();
-    virtual status_t resume();
     virtual void acquireLock();
     virtual void releaseLock();
     virtual status_t setParameter(int key, const Parcel &request);
diff --git a/libvideoeditor/lvpp/YV12ColorConverter.cpp b/libvideoeditor/lvpp/YV12ColorConverter.cpp
new file mode 100755
index 0000000..07fa063
--- /dev/null
+++ b/libvideoeditor/lvpp/YV12ColorConverter.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#include <YV12ColorConverter.h>
+#include <cutils/log.h>
+#include <dlfcn.h>
+
+YV12ColorConverter::YV12ColorConverter() {
+    // Open the shared library
+    mHandle = dlopen("libyv12colorconvert.so", RTLD_NOW);
+
+    if (mHandle == NULL) {
+        LOGW("YV12ColorConverter: cannot load libyv12colorconvert.so");
+        return;
+    }
+
+    // Find the entry point
+    void (*getYV12ColorConverter)(YV12ColorConverter *converter) =
+        (void (*)(YV12ColorConverter*)) dlsym(mHandle, "getYV12ColorConverter");
+
+    if (getYV12ColorConverter == NULL) {
+        LOGW("YV12ColorConverter: cannot load getYV12ColorConverter");
+        dlclose(mHandle);
+        mHandle = NULL;
+        return;
+    }
+
+    // Fill the function pointers.
+    getYV12ColorConverter(this);
+
+    LOGI("YV12ColorConverter: libyv12colorconvert.so loaded");
+}
+
+bool YV12ColorConverter::isLoaded() {
+    return mHandle != NULL;
+}
+
+YV12ColorConverter::~YV12ColorConverter() {
+    if (mHandle) {
+        dlclose(mHandle);
+    }
+}
diff --git a/libvideoeditor/lvpp/YV12ColorConverter.h b/libvideoeditor/lvpp/YV12ColorConverter.h
new file mode 100755
index 0000000..32b52f7
--- /dev/null
+++ b/libvideoeditor/lvpp/YV12ColorConverter.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 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 YV12_COLOR_CONVERTER_H
+#define YV12_COLOR_CONVERTER_H
+
+#include <IYV12ColorConverter.h>
+
+// This is a wrapper around the YV12 color converter functions in
+// IYV12ColorConverter, which is loaded from a shared library.
+class YV12ColorConverter: public IYV12ColorConverter {
+public:
+    YV12ColorConverter();
+    ~YV12ColorConverter();
+
+    // Returns true if the converter functions are successfully loaded.
+    bool isLoaded();
+private:
+    void* mHandle;
+};
+
+#endif /* YV12_COLOR_CONVERTER_H */