/*
 * 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.
 */

// #define LOG_NDEBUG 0
#define LOG_TAG "PreviewController"
#include <utils/Log.h>

#include <gui/Surface.h>

#include "VideoEditorAudioPlayer.h"
#include "PreviewRenderer.h"
#include "M4OSA_Semaphore.h"
#include "M4OSA_Thread.h"
#include "VideoEditorPreviewController.h"

namespace android {


VideoEditorPreviewController::VideoEditorPreviewController()
    : mCurrentPlayer(0),
      mThreadContext(NULL),
      mPlayerState(VePlayerIdle),
      mPrepareReqest(M4OSA_FALSE),
      mClipList(NULL),
      mNumberClipsInStoryBoard(0),
      mNumberClipsToPreview(0),
      mStartingClipIndex(0),
      mPreviewLooping(M4OSA_FALSE),
      mCallBackAfterFrameCnt(0),
      mEffectsSettings(NULL),
      mNumberEffects(0),
      mCurrentClipNumber(-1),
      mClipTotalDuration(0),
      mCurrentVideoEffect(VIDEO_EFFECT_NONE),
      mBackgroundAudioSetting(NULL),
      mAudioMixPCMFileHandle(NULL),
      mTarget(NULL),
      mJniCookie(NULL),
      mJniCallback(NULL),
      mCurrentPlayedDuration(0),
      mCurrentClipDuration(0),
      mVideoStoryBoardTimeMsUptoFirstPreviewClip(0),
      mOverlayState(OVERLAY_CLEAR),
      mActivePlayerIndex(0),
      mOutputVideoWidth(0),
      mOutputVideoHeight(0),
      bStopThreadInProgress(false),
      mSemThreadWait(NULL) {
    ALOGV("VideoEditorPreviewController");
    mRenderingMode = M4xVSS_kBlackBorders;
    mIsFiftiesEffectStarted = false;

    for (int i = 0; i < kTotalNumPlayerInstances; ++i) {
        mVePlayer[i] = NULL;
    }
}

VideoEditorPreviewController::~VideoEditorPreviewController() {
    ALOGV("~VideoEditorPreviewController");
    M4OSA_UInt32 i = 0;
    M4OSA_ERR err = M4NO_ERROR;

    // Stop the thread if its still running
    if(mThreadContext != NULL) {
        err = M4OSA_threadSyncStop(mThreadContext);
        if(err != M4NO_ERROR) {
            ALOGV("~VideoEditorPreviewController: error 0x%x \
            in trying to stop thread", err);
            // Continue even if error
        }

        err = M4OSA_threadSyncClose(mThreadContext);
        if(err != M4NO_ERROR) {
            ALOGE("~VideoEditorPreviewController: error 0x%x \
            in trying to close thread", (unsigned int) err);
            // Continue even if error
        }

        mThreadContext = NULL;
    }

    for (int playerInst=0; playerInst<kTotalNumPlayerInstances;
         playerInst++) {
        if(mVePlayer[playerInst] != NULL) {
            ALOGV("clearing mVePlayer %d", playerInst);
            mVePlayer[playerInst].clear();
        }
    }

    if(mClipList != NULL) {
        // Clean up
        for(i=0;i<mNumberClipsInStoryBoard;i++)
        {
            if(mClipList[i]->pFile != NULL) {
                free(mClipList[i]->pFile);
                mClipList[i]->pFile = NULL;
            }

            free(mClipList[i]);
        }
        free(mClipList);
        mClipList = NULL;
    }

    if(mEffectsSettings) {
        for(i=0;i<mNumberEffects;i++) {
            if(mEffectsSettings[i].xVSS.pFramingBuffer != NULL) {
                free(mEffectsSettings[i].xVSS.pFramingBuffer->pac_data);

                free(mEffectsSettings[i].xVSS.pFramingBuffer);

                mEffectsSettings[i].xVSS.pFramingBuffer = NULL;
            }
        }
        free(mEffectsSettings);
        mEffectsSettings = NULL;
    }

    if (mAudioMixPCMFileHandle) {
        err = M4OSA_fileReadClose (mAudioMixPCMFileHandle);
        mAudioMixPCMFileHandle = M4OSA_NULL;
    }

    if (mBackgroundAudioSetting != NULL) {
        free(mBackgroundAudioSetting);
        mBackgroundAudioSetting = NULL;
    }

    if(mTarget != NULL) {
        delete mTarget;
        mTarget = NULL;
    }

    mOverlayState = OVERLAY_CLEAR;

    ALOGV("~VideoEditorPreviewController returns");
}

M4OSA_ERR VideoEditorPreviewController::loadEditSettings(
    M4VSS3GPP_EditSettings* pSettings,M4xVSS_AudioMixingSettings* bgmSettings) {

    M4OSA_UInt32 i = 0, iClipDuration = 0, rgbSize = 0;
    M4VIFI_UInt8 *tmp = NULL;
    M4OSA_ERR err = M4NO_ERROR;

    ALOGV("loadEditSettings");
    ALOGV("loadEditSettings Channels = %d, sampling Freq %d",
          bgmSettings->uiNbChannels, bgmSettings->uiSamplingFrequency  );
          bgmSettings->uiSamplingFrequency = 32000;

    ALOGV("loadEditSettings Channels = %d, sampling Freq %d",
          bgmSettings->uiNbChannels, bgmSettings->uiSamplingFrequency  );
    Mutex::Autolock autoLock(mLock);

    // Clean up any previous Edit settings before loading new ones
    mCurrentVideoEffect = VIDEO_EFFECT_NONE;

    if(mAudioMixPCMFileHandle) {
        err = M4OSA_fileReadClose (mAudioMixPCMFileHandle);
        mAudioMixPCMFileHandle = M4OSA_NULL;
    }

    if(mBackgroundAudioSetting != NULL) {
        free(mBackgroundAudioSetting);
        mBackgroundAudioSetting = NULL;
    }

    if(mClipList != NULL) {
        // Clean up
        for(i=0;i<mNumberClipsInStoryBoard;i++)
        {
            if(mClipList[i]->pFile != NULL) {
                free(mClipList[i]->pFile);
                mClipList[i]->pFile = NULL;
            }

            free(mClipList[i]);
        }
        free(mClipList);
        mClipList = NULL;
    }

    if(mEffectsSettings) {
        for(i=0;i<mNumberEffects;i++) {
            if(mEffectsSettings[i].xVSS.pFramingBuffer != NULL) {
                free(mEffectsSettings[i].xVSS.pFramingBuffer->pac_data);

                free(mEffectsSettings[i].xVSS.pFramingBuffer);

                mEffectsSettings[i].xVSS.pFramingBuffer = NULL;
            }
        }
        free(mEffectsSettings);
        mEffectsSettings = NULL;
    }

    if(mClipList == NULL) {
        mNumberClipsInStoryBoard = pSettings->uiClipNumber;
        ALOGV("loadEditSettings: # of Clips = %d", mNumberClipsInStoryBoard);

        mClipList = (M4VSS3GPP_ClipSettings**)M4OSA_32bitAlignedMalloc(
         sizeof(M4VSS3GPP_ClipSettings*)*pSettings->uiClipNumber, M4VS,
         (M4OSA_Char*)"LvPP, copy of pClipList");

        if(NULL == mClipList) {
            ALOGE("loadEditSettings: Malloc error");
            return M4ERR_ALLOC;
        }
        memset((void *)mClipList,0,
         sizeof(M4VSS3GPP_ClipSettings*)*pSettings->uiClipNumber);

        for(i=0;i<pSettings->uiClipNumber;i++) {

            // Allocate current clip
            mClipList[i] =
             (M4VSS3GPP_ClipSettings*)M4OSA_32bitAlignedMalloc(
              sizeof(M4VSS3GPP_ClipSettings),M4VS,(M4OSA_Char*)"clip settings");

            if(mClipList[i] == NULL) {

                ALOGE("loadEditSettings: Allocation error for mClipList[%d]", (int)i);
                return M4ERR_ALLOC;
            }
            // Copy plain structure
            memcpy((void *)mClipList[i],
             (void *)pSettings->pClipList[i],
             sizeof(M4VSS3GPP_ClipSettings));

            if(NULL != pSettings->pClipList[i]->pFile) {
                mClipList[i]->pFile = (M4OSA_Char*)M4OSA_32bitAlignedMalloc(
                pSettings->pClipList[i]->filePathSize, M4VS,
                (M4OSA_Char*)"pClipSettingsDest->pFile");

                if(NULL == mClipList[i]->pFile)
                {
                    ALOGE("loadEditSettings : ERROR allocating filename");
                    return M4ERR_ALLOC;
                }

                memcpy((void *)mClipList[i]->pFile,
                 (void *)pSettings->pClipList[i]->pFile,
                 pSettings->pClipList[i]->filePathSize);
            }
            else {
                ALOGE("NULL file path");
                return M4ERR_PARAMETER;
            }

            // Calculate total duration of all clips
            iClipDuration = pSettings->pClipList[i]->uiEndCutTime -
             pSettings->pClipList[i]->uiBeginCutTime;

            mClipTotalDuration = mClipTotalDuration+iClipDuration;
        }
    }

    if(mEffectsSettings == NULL) {
        mNumberEffects = pSettings->nbEffects;
        ALOGV("loadEditSettings: mNumberEffects = %d", mNumberEffects);

        if(mNumberEffects != 0) {
            mEffectsSettings = (M4VSS3GPP_EffectSettings*)M4OSA_32bitAlignedMalloc(
             mNumberEffects*sizeof(M4VSS3GPP_EffectSettings),
             M4VS, (M4OSA_Char*)"effects settings");

            if(mEffectsSettings == NULL) {
                ALOGE("loadEffectsSettings: Allocation error");
                return M4ERR_ALLOC;
            }

            memset((void *)mEffectsSettings,0,
             mNumberEffects*sizeof(M4VSS3GPP_EffectSettings));

            for(i=0;i<mNumberEffects;i++) {

                mEffectsSettings[i].xVSS.pFramingFilePath = NULL;
                mEffectsSettings[i].xVSS.pFramingBuffer = NULL;
                mEffectsSettings[i].xVSS.pTextBuffer = NULL;

                memcpy((void *)&(mEffectsSettings[i]),
                 (void *)&(pSettings->Effects[i]),
                 sizeof(M4VSS3GPP_EffectSettings));

                if(pSettings->Effects[i].VideoEffectType ==
                 (M4VSS3GPP_VideoEffectType)M4xVSS_kVideoEffectType_Framing) {
                    // Allocate the pFraming RGB buffer
                    mEffectsSettings[i].xVSS.pFramingBuffer =
                    (M4VIFI_ImagePlane *)M4OSA_32bitAlignedMalloc(sizeof(M4VIFI_ImagePlane),
                     M4VS, (M4OSA_Char*)"lvpp framing buffer");

                    if(mEffectsSettings[i].xVSS.pFramingBuffer == NULL) {
                        ALOGE("loadEffectsSettings:Alloc error for pFramingBuf");
                        free(mEffectsSettings);
                        mEffectsSettings = NULL;
                        return M4ERR_ALLOC;
                    }

                    // Allocate the pac_data (RGB)
                    if(pSettings->Effects[i].xVSS.rgbType == M4VSS3GPP_kRGB565){
                        rgbSize =
                         pSettings->Effects[i].xVSS.pFramingBuffer->u_width *
                         pSettings->Effects[i].xVSS.pFramingBuffer->u_height*2;
                    }
                    else if(
                     pSettings->Effects[i].xVSS.rgbType == M4VSS3GPP_kRGB888) {
                        rgbSize =
                         pSettings->Effects[i].xVSS.pFramingBuffer->u_width *
                         pSettings->Effects[i].xVSS.pFramingBuffer->u_height*3;
                    }
                    else {
                        ALOGE("loadEffectsSettings: wrong RGB type");
                        free(mEffectsSettings);
                        mEffectsSettings = NULL;
                        return M4ERR_PARAMETER;
                    }

                    tmp = (M4VIFI_UInt8 *)M4OSA_32bitAlignedMalloc(rgbSize, M4VS,
                     (M4OSA_Char*)"framing buffer pac_data");

                    if(tmp == NULL) {
                        ALOGE("loadEffectsSettings:Alloc error pFramingBuf pac");
                        free(mEffectsSettings);
                        mEffectsSettings = NULL;
                        free(mEffectsSettings[i].xVSS.pFramingBuffer);

                        mEffectsSettings[i].xVSS.pFramingBuffer = NULL;
                        return M4ERR_ALLOC;
                    }
                    /* Initialize the pFramingBuffer*/
                    mEffectsSettings[i].xVSS.pFramingBuffer->pac_data = tmp;
                    mEffectsSettings[i].xVSS.pFramingBuffer->u_height =
                     pSettings->Effects[i].xVSS.pFramingBuffer->u_height;

                    mEffectsSettings[i].xVSS.pFramingBuffer->u_width =
                     pSettings->Effects[i].xVSS.pFramingBuffer->u_width;

                    mEffectsSettings[i].xVSS.pFramingBuffer->u_stride =
                     pSettings->Effects[i].xVSS.pFramingBuffer->u_stride;

                    mEffectsSettings[i].xVSS.pFramingBuffer->u_topleft =
                     pSettings->Effects[i].xVSS.pFramingBuffer->u_topleft;

                    mEffectsSettings[i].xVSS.uialphaBlendingStart =
                     pSettings->Effects[i].xVSS.uialphaBlendingStart;

                    mEffectsSettings[i].xVSS.uialphaBlendingMiddle =
                     pSettings->Effects[i].xVSS.uialphaBlendingMiddle;

                    mEffectsSettings[i].xVSS.uialphaBlendingEnd =
                     pSettings->Effects[i].xVSS.uialphaBlendingEnd;

                    mEffectsSettings[i].xVSS.uialphaBlendingFadeInTime =
                     pSettings->Effects[i].xVSS.uialphaBlendingFadeInTime;
                    mEffectsSettings[i].xVSS.uialphaBlendingFadeOutTime =
                     pSettings->Effects[i].xVSS.uialphaBlendingFadeOutTime;

                    // Copy the pFraming data
                    memcpy((void *)
                    mEffectsSettings[i].xVSS.pFramingBuffer->pac_data,
                    (void *)pSettings->Effects[i].xVSS.pFramingBuffer->pac_data,
                    rgbSize);

                    mEffectsSettings[i].xVSS.rgbType =
                     pSettings->Effects[i].xVSS.rgbType;
                }
            }
        }
    }

    if (mBackgroundAudioSetting == NULL) {

        mBackgroundAudioSetting = (M4xVSS_AudioMixingSettings*)M4OSA_32bitAlignedMalloc(
        sizeof(M4xVSS_AudioMixingSettings), M4VS,
        (M4OSA_Char*)"LvPP, copy of bgmSettings");

        if(NULL == mBackgroundAudioSetting) {
            ALOGE("loadEditSettings: mBackgroundAudioSetting Malloc failed");
            return M4ERR_ALLOC;
        }

        memset((void *)mBackgroundAudioSetting, 0,sizeof(M4xVSS_AudioMixingSettings*));
        memcpy((void *)mBackgroundAudioSetting, (void *)bgmSettings, sizeof(M4xVSS_AudioMixingSettings));

        if ( mBackgroundAudioSetting->pFile != M4OSA_NULL ) {

            mBackgroundAudioSetting->pFile = (M4OSA_Void*) bgmSettings->pPCMFilePath;
            mBackgroundAudioSetting->uiNbChannels = 2;
            mBackgroundAudioSetting->uiSamplingFrequency = 32000;
        }

        // Open the BG file
        if ( mBackgroundAudioSetting->pFile != M4OSA_NULL ) {
            err = M4OSA_fileReadOpen(&mAudioMixPCMFileHandle,
             mBackgroundAudioSetting->pFile, M4OSA_kFileRead);

            if (err != M4NO_ERROR) {
                ALOGE("loadEditSettings: mBackgroundAudio PCM File open failed");
                return M4ERR_PARAMETER;
            }
        }
    }

    mOutputVideoSize = pSettings->xVSS.outputVideoSize;
    mFrameStr.pBuffer = M4OSA_NULL;
    return M4NO_ERROR;
}

M4OSA_ERR VideoEditorPreviewController::setSurface(const sp<Surface> &surface) {
    ALOGV("setSurface");
    Mutex::Autolock autoLock(mLock);

    mSurface = surface;
    return M4NO_ERROR;
}

M4OSA_ERR VideoEditorPreviewController::startPreview(
    M4OSA_UInt32 fromMS, M4OSA_Int32 toMs, M4OSA_UInt16 callBackAfterFrameCount,
    M4OSA_Bool loop) {

    M4OSA_ERR err = M4NO_ERROR;
    M4OSA_UInt32 i = 0, iIncrementedDuration = 0;
    ALOGV("startPreview");

    if(fromMS > (M4OSA_UInt32)toMs) {
        ALOGE("startPreview: fromMS > toMs");
        return M4ERR_PARAMETER;
    }

    if(toMs == 0) {
        ALOGE("startPreview: toMs is 0");
        return M4ERR_PARAMETER;
    }

    // If already started, then stop preview first
    for(int playerInst=0; playerInst<kTotalNumPlayerInstances; playerInst++) {
        if(mVePlayer[playerInst] != NULL) {
            ALOGV("startPreview: stopping previously started preview playback");
            stopPreview();
            break;
        }
    }

    // If renderPreview was called previously, then delete Renderer object first
    if(mTarget != NULL) {
        ALOGV("startPreview: delete previous PreviewRenderer");
        delete mTarget;
        mTarget = NULL;
    }

    // Create Audio player to be used for entire
    // storyboard duration
    mVEAudioSink = new VideoEditorPlayer::VeAudioOutput();
    mVEAudioPlayer = new VideoEditorAudioPlayer(mVEAudioSink);
    mVEAudioPlayer->setAudioMixSettings(mBackgroundAudioSetting);
    mVEAudioPlayer->setAudioMixPCMFileHandle(mAudioMixPCMFileHandle);

    // Create Video Renderer to be used for the entire storyboard duration.
    uint32_t width, height;
    getVideoSizeByResolution(mOutputVideoSize, &width, &height);
    mNativeWindowRenderer = new NativeWindowRenderer(mSurface, width, height);

    ALOGV("startPreview: loop = %d", loop);
    mPreviewLooping = loop;

    ALOGV("startPreview: callBackAfterFrameCount = %d", callBackAfterFrameCount);
    mCallBackAfterFrameCnt = callBackAfterFrameCount;

    for (int playerInst=0; playerInst<kTotalNumPlayerInstances; playerInst++) {
        mVePlayer[playerInst] = new VideoEditorPlayer(mNativeWindowRenderer);
        if(mVePlayer[playerInst] == NULL) {
            ALOGE("startPreview:Error creating VideoEditorPlayer %d",playerInst);
            return M4ERR_ALLOC;
        }
        ALOGV("startPreview: object created");

        mVePlayer[playerInst]->setNotifyCallback(this,(notify_callback_f)notify);
        ALOGV("startPreview: notify callback set");

        mVePlayer[playerInst]->loadEffectsSettings(mEffectsSettings,
         mNumberEffects);
        ALOGV("startPreview: effects settings loaded");

        mVePlayer[playerInst]->loadAudioMixSettings(mBackgroundAudioSetting);
        ALOGV("startPreview: AudioMixSettings settings loaded");

        mVePlayer[playerInst]->setAudioMixPCMFileHandle(mAudioMixPCMFileHandle);
        ALOGV("startPreview: AudioMixPCMFileHandle set");

        mVePlayer[playerInst]->setProgressCallbackInterval(
         mCallBackAfterFrameCnt);
        ALOGV("startPreview: setProgressCallBackInterval");
    }

    mPlayerState = VePlayerIdle;
    mPrepareReqest = M4OSA_FALSE;

    if(fromMS == 0) {
        mCurrentClipNumber = -1;
        // Save original value
        mFirstPreviewClipBeginTime = mClipList[0]->uiBeginCutTime;
        mVideoStoryBoardTimeMsUptoFirstPreviewClip = 0;
    }
    else {
        ALOGV("startPreview: fromMS=%d", fromMS);
        if(fromMS >= mClipTotalDuration) {
            ALOGE("startPreview: fromMS >= mClipTotalDuration");
            return M4ERR_PARAMETER;
        }
        for(i=0;i<mNumberClipsInStoryBoard;i++) {
            if(fromMS < (iIncrementedDuration + (mClipList[i]->uiEndCutTime -
             mClipList[i]->uiBeginCutTime))) {
                // Set to 1 index below,
                // as threadProcess first increments the clip index
                // and then processes clip in thread loop
                mCurrentClipNumber = i-1;
                ALOGD("startPreview:mCurrentClipNumber = %d fromMS=%d",i,fromMS);

                // Save original value
                mFirstPreviewClipBeginTime = mClipList[i]->uiBeginCutTime;

                // Set correct begin time to start playback
                if((fromMS+mClipList[i]->uiBeginCutTime) >
                (iIncrementedDuration+mClipList[i]->uiBeginCutTime)) {

                    mClipList[i]->uiBeginCutTime =
                     mClipList[i]->uiBeginCutTime +
                     (fromMS - iIncrementedDuration);
                }
                break;
            }
            else {
                iIncrementedDuration = iIncrementedDuration +
                 (mClipList[i]->uiEndCutTime - mClipList[i]->uiBeginCutTime);
            }
        }
        mVideoStoryBoardTimeMsUptoFirstPreviewClip = iIncrementedDuration;
    }

    for (int playerInst=0; playerInst<kTotalNumPlayerInstances; playerInst++) {
        mVePlayer[playerInst]->setAudioMixStoryBoardParam(fromMS,
         mFirstPreviewClipBeginTime,
         mClipList[i]->ClipProperties.uiClipAudioVolumePercentage);

        ALOGV("startPreview:setAudioMixStoryBoardSkimTimeStamp set %d cuttime \
         %d", fromMS, mFirstPreviewClipBeginTime);
    }

    mStartingClipIndex = mCurrentClipNumber+1;

    // Start playing with player instance 0
    mCurrentPlayer = 0;
    mActivePlayerIndex = 0;

    if(toMs == -1) {
        ALOGV("startPreview: Preview till end of storyboard");
        mNumberClipsToPreview = mNumberClipsInStoryBoard;
        // Save original value
        mLastPreviewClipEndTime =
         mClipList[mNumberClipsToPreview-1]->uiEndCutTime;
    }
    else {
        ALOGV("startPreview: toMs=%d", toMs);
        if((M4OSA_UInt32)toMs > mClipTotalDuration) {
            ALOGE("startPreview: toMs > mClipTotalDuration");
            return M4ERR_PARAMETER;
        }

        iIncrementedDuration = 0;

        for(i=0;i<mNumberClipsInStoryBoard;i++) {
            if((M4OSA_UInt32)toMs <= (iIncrementedDuration +
             (mClipList[i]->uiEndCutTime - mClipList[i]->uiBeginCutTime))) {
                // Save original value
                mLastPreviewClipEndTime = mClipList[i]->uiEndCutTime;
                // Set the end cut time of clip index i to toMs
                mClipList[i]->uiEndCutTime = toMs;

                // Number of clips to be previewed is from index 0 to i
                // increment by 1 as i starts from 0
                mNumberClipsToPreview = i+1;
                break;
            }
            else {
                iIncrementedDuration = iIncrementedDuration +
                 (mClipList[i]->uiEndCutTime - mClipList[i]->uiBeginCutTime);
            }
        }
    }

    // Open the thread semaphore
    M4OSA_semaphoreOpen(&mSemThreadWait, 1);

    // Open the preview process thread
    err = M4OSA_threadSyncOpen(&mThreadContext, (M4OSA_ThreadDoIt)threadProc);
    if (M4NO_ERROR != err) {
        ALOGE("VideoEditorPreviewController:M4OSA_threadSyncOpen error %d", (int) err);
        return err;
    }

    // Set the stacksize
    err = M4OSA_threadSyncSetOption(mThreadContext, M4OSA_ThreadStackSize,
     (M4OSA_DataOption) kPreviewThreadStackSize);

    if (M4NO_ERROR != err) {
        ALOGE("VideoEditorPreviewController: threadSyncSetOption error %d", (int) err);
        M4OSA_threadSyncClose(mThreadContext);
        mThreadContext = NULL;
        return err;
    }

     // Start the thread
     err = M4OSA_threadSyncStart(mThreadContext, (M4OSA_Void*)this);
     if (M4NO_ERROR != err) {
        ALOGE("VideoEditorPreviewController: threadSyncStart error %d", (int) err);
        M4OSA_threadSyncClose(mThreadContext);
        mThreadContext = NULL;
        return err;
    }
    bStopThreadInProgress = false;

    ALOGV("startPreview: process thread started");
    return M4NO_ERROR;
}

M4OSA_UInt32 VideoEditorPreviewController::stopPreview() {
    M4OSA_ERR err = M4NO_ERROR;
    uint32_t lastRenderedFrameTimeMs = 0;
    ALOGV("stopPreview");

    // Stop the thread
    if(mThreadContext != NULL) {
        bStopThreadInProgress = true;
        {
            Mutex::Autolock autoLock(mLockSem);
            if (mSemThreadWait != NULL) {
                err = M4OSA_semaphorePost(mSemThreadWait);
            }
        }

        err = M4OSA_threadSyncStop(mThreadContext);
        if(err != M4NO_ERROR) {
            ALOGV("stopPreview: error 0x%x in trying to stop thread", err);
            // Continue even if error
        }

        err = M4OSA_threadSyncClose(mThreadContext);
        if(err != M4NO_ERROR) {
            ALOGE("stopPreview: error 0x%x in trying to close thread", (unsigned int)err);
            // Continue even if error
        }

        mThreadContext = NULL;
    }

    // Close the semaphore first
    {
        Mutex::Autolock autoLock(mLockSem);
        if(mSemThreadWait != NULL) {
            err = M4OSA_semaphoreClose(mSemThreadWait);
            ALOGV("stopPreview: close semaphore returns 0x%x", err);
            mSemThreadWait = NULL;
        }
    }

    for (int playerInst=0; playerInst<kTotalNumPlayerInstances; playerInst++) {
        if(mVePlayer[playerInst] != NULL) {
            if(mVePlayer[playerInst]->isPlaying()) {
                ALOGV("stop the player first");
                mVePlayer[playerInst]->stop();
            }
            if (playerInst == mActivePlayerIndex) {
                // Return the last rendered frame time stamp
                mVePlayer[mActivePlayerIndex]->getLastRenderedTimeMs(&lastRenderedFrameTimeMs);
            }

            //This is used to syncronize onStreamDone() in PreviewPlayer and
            //stopPreview() in PreviewController
            sp<VideoEditorPlayer> temp = mVePlayer[playerInst];
            temp->acquireLock();
            ALOGV("stopPreview: clearing mVePlayer");
            mVePlayer[playerInst].clear();
            mVePlayer[playerInst] = NULL;
            temp->releaseLock();
        }
    }
    ALOGV("stopPreview: clear audioSink and audioPlayer");
    mVEAudioSink.clear();
    if (mVEAudioPlayer) {
        delete mVEAudioPlayer;
        mVEAudioPlayer = NULL;
    }

    delete mNativeWindowRenderer;
    mNativeWindowRenderer = NULL;

    // If image file playing, then free the buffer pointer
    if(mFrameStr.pBuffer != M4OSA_NULL) {
        free(mFrameStr.pBuffer);
        mFrameStr.pBuffer = M4OSA_NULL;
    }

    // Reset original begin cuttime of first previewed clip*/
    mClipList[mStartingClipIndex]->uiBeginCutTime = mFirstPreviewClipBeginTime;
    // Reset original end cuttime of last previewed clip*/
    mClipList[mNumberClipsToPreview-1]->uiEndCutTime = mLastPreviewClipEndTime;

    mPlayerState = VePlayerIdle;
    mPrepareReqest = M4OSA_FALSE;

    mCurrentPlayedDuration = 0;
    mCurrentClipDuration = 0;
    mRenderingMode = M4xVSS_kBlackBorders;
    mOutputVideoWidth = 0;
    mOutputVideoHeight = 0;

    ALOGV("stopPreview() lastRenderedFrameTimeMs %ld", lastRenderedFrameTimeMs);
    return lastRenderedFrameTimeMs;
}

M4OSA_ERR VideoEditorPreviewController::clearSurface(
    const sp<Surface> &surface, VideoEditor_renderPreviewFrameStr* pFrameInfo) {

    M4OSA_ERR err = M4NO_ERROR;
    VideoEditor_renderPreviewFrameStr* pFrameStr = pFrameInfo;
    M4OSA_UInt32 outputBufferWidth =0, outputBufferHeight=0;
    M4VIFI_ImagePlane planeOut[3];
    ALOGV("Inside preview clear frame");

    Mutex::Autolock autoLock(mLock);

    // Delete previous renderer instance
    if(mTarget != NULL) {
        delete mTarget;
        mTarget = NULL;
    }

    outputBufferWidth = pFrameStr->uiFrameWidth;
    outputBufferHeight = pFrameStr->uiFrameHeight;

    // Initialize the renderer
    if(mTarget == NULL) {

        mTarget = PreviewRenderer::CreatePreviewRenderer(
            surface,
            outputBufferWidth, outputBufferHeight);

        if(mTarget == NULL) {
            ALOGE("renderPreviewFrame: cannot create PreviewRenderer");
            return M4ERR_ALLOC;
        }
    }

    // Out plane
    uint8_t* outBuffer;
    size_t outBufferStride = 0;

    ALOGV("doMediaRendering CALL getBuffer()");
    mTarget->getBufferYV12(&outBuffer, &outBufferStride);

    // Set the output YUV420 plane to be compatible with YV12 format
    //In YV12 format, sizes must be even
    M4OSA_UInt32 yv12PlaneWidth = ((outputBufferWidth +1)>>1)<<1;
    M4OSA_UInt32 yv12PlaneHeight = ((outputBufferHeight+1)>>1)<<1;

    prepareYV12ImagePlane(planeOut, yv12PlaneWidth, yv12PlaneHeight,
     (M4OSA_UInt32)outBufferStride, (M4VIFI_UInt8 *)outBuffer);

    /* Fill the surface with black frame */
    memset((void *)planeOut[0].pac_data,0x00,planeOut[0].u_width *
                            planeOut[0].u_height * 1.5);
    memset((void *)planeOut[1].pac_data,128,planeOut[1].u_width *
                            planeOut[1].u_height);
    memset((void *)planeOut[2].pac_data,128,planeOut[2].u_width *
                             planeOut[2].u_height);

    mTarget->renderYV12();
    return err;
}

M4OSA_ERR VideoEditorPreviewController::renderPreviewFrame(
            const sp<Surface> &surface,
            VideoEditor_renderPreviewFrameStr* pFrameInfo,
            VideoEditorCurretEditInfo *pCurrEditInfo) {

    M4OSA_ERR err = M4NO_ERROR;
    M4OSA_UInt32 i = 0, iIncrementedDuration = 0, tnTimeMs=0, framesize =0;
    VideoEditor_renderPreviewFrameStr* pFrameStr = pFrameInfo;
    M4VIFI_UInt8 *pixelArray = NULL;
    Mutex::Autolock autoLock(mLock);

    if (pCurrEditInfo != NULL) {
        pCurrEditInfo->overlaySettingsIndex = -1;
    }
    // Delete previous renderer instance
    if(mTarget != NULL) {
        delete mTarget;
        mTarget = NULL;
    }

    if(mOutputVideoWidth == 0) {
        mOutputVideoWidth = pFrameStr->uiFrameWidth;
    }

    if(mOutputVideoHeight == 0) {
        mOutputVideoHeight = pFrameStr->uiFrameHeight;
    }

    // Initialize the renderer
    if(mTarget == NULL) {
         mTarget = PreviewRenderer::CreatePreviewRenderer(
            surface,
            mOutputVideoWidth, mOutputVideoHeight);

        if(mTarget == NULL) {
            ALOGE("renderPreviewFrame: cannot create PreviewRenderer");
            return M4ERR_ALLOC;
        }
    }

    pixelArray = NULL;

    // Apply rotation if required
    if (pFrameStr->videoRotationDegree != 0) {
        err = applyVideoRotation((M4OSA_Void *)pFrameStr->pBuffer,
                  pFrameStr->uiFrameWidth, pFrameStr->uiFrameHeight,
                  pFrameStr->videoRotationDegree);
        if (M4NO_ERROR != err) {
            ALOGE("renderPreviewFrame: cannot rotate video, err 0x%x", (unsigned int)err);
            delete mTarget;
            mTarget = NULL;
            return err;
        } else {
           // Video rotation done.
           // Swap width and height if 90 or 270 degrees
           if (pFrameStr->videoRotationDegree != 180) {
               int32_t temp = pFrameStr->uiFrameWidth;
               pFrameStr->uiFrameWidth = pFrameStr->uiFrameHeight;
               pFrameStr->uiFrameHeight = temp;
           }
        }
    }
    // Postprocessing (apply video effect)
    if(pFrameStr->bApplyEffect == M4OSA_TRUE) {

        for(i=0;i<mNumberEffects;i++) {
            // First check if effect starttime matches the clip being previewed
            if((mEffectsSettings[i].uiStartTime < pFrameStr->clipBeginCutTime)
             ||(mEffectsSettings[i].uiStartTime >= pFrameStr->clipEndCutTime)) {
                // This effect doesn't belong to this clip, check next one
                continue;
            }
            if((mEffectsSettings[i].uiStartTime <= pFrameStr->timeMs) &&
            ((mEffectsSettings[i].uiStartTime+mEffectsSettings[i].uiDuration) >=
             pFrameStr->timeMs) && (mEffectsSettings[i].uiDuration != 0)) {
                setVideoEffectType(mEffectsSettings[i].VideoEffectType, TRUE);
            }
            else {
                setVideoEffectType(mEffectsSettings[i].VideoEffectType, FALSE);
            }
        }

        //Provide the overlay Update indication when there is an overlay effect
        if (mCurrentVideoEffect & VIDEO_EFFECT_FRAMING) {
            M4OSA_UInt32 index;
            mCurrentVideoEffect &= ~VIDEO_EFFECT_FRAMING; //never apply framing here.

            // Find the effect in effectSettings array
            for (index = 0; index < mNumberEffects; index++) {
                if(mEffectsSettings[index].VideoEffectType ==
                    (M4VSS3GPP_VideoEffectType)M4xVSS_kVideoEffectType_Framing) {

                    if((mEffectsSettings[index].uiStartTime <= pFrameInfo->timeMs) &&
                        ((mEffectsSettings[index].uiStartTime+
                        mEffectsSettings[index].uiDuration) >= pFrameInfo->timeMs))
                    {
                        break;
                    }
                }
            }
            if ((index < mNumberEffects) && (pCurrEditInfo != NULL)) {
                pCurrEditInfo->overlaySettingsIndex = index;
                ALOGV("Framing index = %d", index);
            } else {
                ALOGV("No framing effects found");
            }
        }

        if(mCurrentVideoEffect != VIDEO_EFFECT_NONE) {
            err = applyVideoEffect((M4OSA_Void *)pFrameStr->pBuffer,
             OMX_COLOR_FormatYUV420Planar, pFrameStr->uiFrameWidth,
             pFrameStr->uiFrameHeight, pFrameStr->timeMs,
             (M4OSA_Void *)pixelArray);

            if(err != M4NO_ERROR) {
                ALOGE("renderPreviewFrame: applyVideoEffect error 0x%x", (unsigned int)err);
                delete mTarget;
                mTarget = NULL;
                free(pixelArray);
                pixelArray = NULL;
                return err;
           }
           mCurrentVideoEffect = VIDEO_EFFECT_NONE;
        }
        else {
            // Apply the rendering mode
            err = doImageRenderingMode((M4OSA_Void *)pFrameStr->pBuffer,
             OMX_COLOR_FormatYUV420Planar, pFrameStr->uiFrameWidth,
             pFrameStr->uiFrameHeight, (M4OSA_Void *)pixelArray);

            if(err != M4NO_ERROR) {
                ALOGE("renderPreviewFrame:doImageRenderingMode error 0x%x", (unsigned int)err);
                delete mTarget;
                mTarget = NULL;
                free(pixelArray);
                pixelArray = NULL;
                return err;
            }
        }
    }
    else {
        // Apply the rendering mode
        err = doImageRenderingMode((M4OSA_Void *)pFrameStr->pBuffer,
         OMX_COLOR_FormatYUV420Planar, pFrameStr->uiFrameWidth,
         pFrameStr->uiFrameHeight, (M4OSA_Void *)pixelArray);

        if(err != M4NO_ERROR) {
            ALOGE("renderPreviewFrame: doImageRenderingMode error 0x%x", (unsigned int)err);
            delete mTarget;
            mTarget = NULL;
            free(pixelArray);
            pixelArray = NULL;
            return err;
        }
    }

    mTarget->renderYV12();
    return err;
}

M4OSA_Void VideoEditorPreviewController::setJniCallback(void* cookie,
    jni_progress_callback_fct callbackFct) {
    //ALOGV("setJniCallback");
    mJniCookie = cookie;
    mJniCallback = callbackFct;
}

M4OSA_ERR VideoEditorPreviewController::preparePlayer(
    void* param, int playerInstance, int index) {

    M4OSA_ERR err = M4NO_ERROR;
    VideoEditorPreviewController *pController =
     (VideoEditorPreviewController *)param;

    ALOGV("preparePlayer: instance %d file %d", playerInstance, index);

    const char* fileName = (const char*) pController->mClipList[index]->pFile;
    pController->mVePlayer[playerInstance]->setDataSource(fileName, NULL);

    ALOGV("preparePlayer: setDataSource instance %s",
     (const char *)pController->mClipList[index]->pFile);

    pController->mVePlayer[playerInstance]->setVideoSurface(
     pController->mSurface);
    ALOGV("preparePlayer: setVideoSurface");

    pController->mVePlayer[playerInstance]->setMediaRenderingMode(
     pController->mClipList[index]->xVSS.MediaRendering,
     pController->mOutputVideoSize);
    ALOGV("preparePlayer: setMediaRenderingMode");

    if((M4OSA_UInt32)index == pController->mStartingClipIndex) {
        pController->mVePlayer[playerInstance]->setPlaybackBeginTime(
        pController->mFirstPreviewClipBeginTime);
    }
    else {
        pController->mVePlayer[playerInstance]->setPlaybackBeginTime(
        pController->mClipList[index]->uiBeginCutTime);
    }
    ALOGV("preparePlayer: setPlaybackBeginTime(%d)",
     pController->mClipList[index]->uiBeginCutTime);

    pController->mVePlayer[playerInstance]->setPlaybackEndTime(
     pController->mClipList[index]->uiEndCutTime);
    ALOGV("preparePlayer: setPlaybackEndTime(%d)",
     pController->mClipList[index]->uiEndCutTime);

    if(pController->mClipList[index]->FileType == M4VIDEOEDITING_kFileType_ARGB8888) {
        pController->mVePlayer[playerInstance]->setImageClipProperties(
                 pController->mClipList[index]->ClipProperties.uiVideoWidth,
                 pController->mClipList[index]->ClipProperties.uiVideoHeight);
        ALOGV("preparePlayer: setImageClipProperties");
    }

    pController->mVePlayer[playerInstance]->prepare();
    ALOGV("preparePlayer: prepared");

    if(pController->mClipList[index]->uiBeginCutTime > 0) {
        pController->mVePlayer[playerInstance]->seekTo(
         pController->mClipList[index]->uiBeginCutTime);

        ALOGV("preparePlayer: seekTo(%d)",
         pController->mClipList[index]->uiBeginCutTime);
    }
    pController->mVePlayer[pController->mCurrentPlayer]->setAudioPlayer(pController->mVEAudioPlayer);

    pController->mVePlayer[playerInstance]->readFirstVideoFrame();
    ALOGV("preparePlayer: readFirstVideoFrame of clip");

    return err;
}

M4OSA_ERR VideoEditorPreviewController::threadProc(M4OSA_Void* param) {
    M4OSA_ERR err = M4NO_ERROR;
    M4OSA_Int32 index = 0;
    VideoEditorPreviewController *pController =
     (VideoEditorPreviewController *)param;

    ALOGV("inside threadProc");
    if(pController->mPlayerState == VePlayerIdle) {
        (pController->mCurrentClipNumber)++;

        ALOGD("threadProc: playing file index %d total clips %d",
         pController->mCurrentClipNumber, pController->mNumberClipsToPreview);

        if((M4OSA_UInt32)pController->mCurrentClipNumber >=
         pController->mNumberClipsToPreview) {

            ALOGD("All clips previewed");

            pController->mCurrentPlayedDuration = 0;
            pController->mCurrentClipDuration = 0;
            pController->mCurrentPlayer = 0;

            if(pController->mPreviewLooping == M4OSA_TRUE) {
                pController->mCurrentClipNumber =
                 pController->mStartingClipIndex;

                ALOGD("Preview looping TRUE, restarting from clip index %d",
                 pController->mCurrentClipNumber);

                // Reset the story board timestamp inside the player
                for (int playerInst=0; playerInst<kTotalNumPlayerInstances;
                 playerInst++) {
                    pController->mVePlayer[playerInst]->resetJniCallbackTimeStamp();
                }
            }
            else {
                M4OSA_UInt32 endArgs = 0;
                if(pController->mJniCallback != NULL) {
                    pController->mJniCallback(
                     pController->mJniCookie, MSG_TYPE_PREVIEW_END, &endArgs);
                }
                pController->mPlayerState = VePlayerAutoStop;

                // Reset original begin cuttime of first previewed clip
                pController->mClipList[pController->mStartingClipIndex]->uiBeginCutTime =
                 pController->mFirstPreviewClipBeginTime;
                // Reset original end cuttime of last previewed clip
                pController->mClipList[pController->mNumberClipsToPreview-1]->uiEndCutTime =
                 pController->mLastPreviewClipEndTime;

                // Return a warning to M4OSA thread handler
                // so that thread is moved from executing state to open state
                return M4WAR_NO_MORE_STREAM;
            }
        }

        index=pController->mCurrentClipNumber;
        if((M4OSA_UInt32)pController->mCurrentClipNumber == pController->mStartingClipIndex) {
            pController->mCurrentPlayedDuration +=
             pController->mVideoStoryBoardTimeMsUptoFirstPreviewClip;

            pController->mCurrentClipDuration =
             pController->mClipList[pController->mCurrentClipNumber]->uiEndCutTime
              - pController->mFirstPreviewClipBeginTime;

            preparePlayer((void*)pController, pController->mCurrentPlayer, index);
        }
        else {
            pController->mCurrentPlayedDuration +=
             pController->mCurrentClipDuration;

            pController->mCurrentClipDuration =
             pController->mClipList[pController->mCurrentClipNumber]->uiEndCutTime -
             pController->mClipList[pController->mCurrentClipNumber]->uiBeginCutTime;
        }

        pController->mVePlayer[pController->mCurrentPlayer]->setStoryboardStartTime(
         pController->mCurrentPlayedDuration);
        ALOGV("threadProc: setStoryboardStartTime");

        // Set the next clip duration for Audio mix here
        if((M4OSA_UInt32)pController->mCurrentClipNumber != pController->mStartingClipIndex) {

            pController->mVePlayer[pController->mCurrentPlayer]->setAudioMixStoryBoardParam(
             pController->mCurrentPlayedDuration,
             pController->mClipList[index]->uiBeginCutTime,
             pController->mClipList[index]->ClipProperties.uiClipAudioVolumePercentage);

            ALOGV("threadProc: setAudioMixStoryBoardParam fromMS %d \
             ClipBeginTime %d", pController->mCurrentPlayedDuration +
             pController->mClipList[index]->uiBeginCutTime,
             pController->mClipList[index]->uiBeginCutTime,
             pController->mClipList[index]->ClipProperties.uiClipAudioVolumePercentage);
        }
        // Capture the active player being used
        pController->mActivePlayerIndex = pController->mCurrentPlayer;

        pController->mVePlayer[pController->mCurrentPlayer]->start();
        ALOGV("threadProc: started");

        pController->mPlayerState = VePlayerBusy;

    } else if(pController->mPlayerState == VePlayerAutoStop) {
        ALOGV("Preview completed..auto stop the player");
    } else if ((pController->mPlayerState == VePlayerBusy) && (pController->mPrepareReqest)) {
        // Prepare the player here
        pController->mPrepareReqest = M4OSA_FALSE;
        preparePlayer((void*)pController, pController->mCurrentPlayer,
            pController->mCurrentClipNumber+1);
        if (pController->mSemThreadWait != NULL) {
            err = M4OSA_semaphoreWait(pController->mSemThreadWait,
                M4OSA_WAIT_FOREVER);
        }
    } else {
        if (!pController->bStopThreadInProgress) {
            ALOGV("threadProc: state busy...wait for sem");
            if (pController->mSemThreadWait != NULL) {
                err = M4OSA_semaphoreWait(pController->mSemThreadWait,
                 M4OSA_WAIT_FOREVER);
             }
        }
        ALOGV("threadProc: sem wait returned err = 0x%x", err);
    }

    //Always return M4NO_ERROR to ensure the thread keeps running
    return M4NO_ERROR;
}

void VideoEditorPreviewController::notify(
    void* cookie, int msg, int ext1, int ext2)
{
    VideoEditorPreviewController *pController =
     (VideoEditorPreviewController *)cookie;

    M4OSA_ERR err = M4NO_ERROR;
    uint32_t clipDuration = 0;
    switch (msg) {
        case MEDIA_NOP: // interface test message
            ALOGV("MEDIA_NOP");
            break;
        case MEDIA_PREPARED:
            ALOGV("MEDIA_PREPARED");
            break;
        case MEDIA_PLAYBACK_COMPLETE:
        {
            ALOGD("notify:MEDIA_PLAYBACK_COMPLETE, mCurrentClipNumber = %d",
                    pController->mCurrentClipNumber);
            pController->mPlayerState = VePlayerIdle;

            //send progress callback with last frame timestamp
            if((M4OSA_UInt32)pController->mCurrentClipNumber ==
             pController->mStartingClipIndex) {
                clipDuration =
                 pController->mClipList[pController->mCurrentClipNumber]->uiEndCutTime
                  - pController->mFirstPreviewClipBeginTime;
            }
            else {
                clipDuration =
                 pController->mClipList[pController->mCurrentClipNumber]->uiEndCutTime
                  - pController->mClipList[pController->mCurrentClipNumber]->uiBeginCutTime;
            }

            M4OSA_UInt32 playedDuration = clipDuration+pController->mCurrentPlayedDuration;
            pController->mJniCallback(
                 pController->mJniCookie, MSG_TYPE_PROGRESS_INDICATION,
                 &playedDuration);

            if ((pController->mOverlayState == OVERLAY_UPDATE) &&
                ((M4OSA_UInt32)pController->mCurrentClipNumber !=
                (pController->mNumberClipsToPreview-1))) {
                VideoEditorCurretEditInfo *pEditInfo =
                    (VideoEditorCurretEditInfo*)M4OSA_32bitAlignedMalloc(sizeof(VideoEditorCurretEditInfo),
                    M4VS, (M4OSA_Char*)"Current Edit info");
                pEditInfo->overlaySettingsIndex = ext2;
                pEditInfo->clipIndex = pController->mCurrentClipNumber;
                pController->mOverlayState == OVERLAY_CLEAR;
                if (pController->mJniCallback != NULL) {
                        pController->mJniCallback(pController->mJniCookie,
                            MSG_TYPE_OVERLAY_CLEAR, pEditInfo);
                }
                free(pEditInfo);
            }
            {
                Mutex::Autolock autoLock(pController->mLockSem);
                if (pController->mSemThreadWait != NULL) {
                    M4OSA_semaphorePost(pController->mSemThreadWait);
                    return;
                }
            }

            break;
        }
        case MEDIA_ERROR:
        {
            int err_val = ext1;
          // Always log errors.
          // ext1: Media framework error code.
          // ext2: Implementation dependant error code.
            ALOGE("MEDIA_ERROR; error (%d, %d)", ext1, ext2);
            if(pController->mJniCallback != NULL) {
                pController->mJniCallback(pController->mJniCookie,
                 MSG_TYPE_PLAYER_ERROR, &err_val);
            }
            break;
        }
        case MEDIA_INFO:
        {
            int info_val = ext2;
            // ext1: Media framework error code.
            // ext2: Implementation dependant error code.
            //ALOGW("MEDIA_INFO; info/warning (%d, %d)", ext1, ext2);
            if(pController->mJniCallback != NULL) {
                pController->mJniCallback(pController->mJniCookie,
                 MSG_TYPE_PROGRESS_INDICATION, &info_val);
            }
            break;
        }
        case MEDIA_SEEK_COMPLETE:
            ALOGV("MEDIA_SEEK_COMPLETE; Received seek complete");
            break;
        case MEDIA_BUFFERING_UPDATE:
            ALOGV("MEDIA_BUFFERING_UPDATE; buffering %d", ext1);
            break;
        case MEDIA_SET_VIDEO_SIZE:
            ALOGV("MEDIA_SET_VIDEO_SIZE; New video size %d x %d", ext1, ext2);
            break;
        case 0xAAAAAAAA:
            ALOGV("VIDEO PLAYBACK ALMOST over, prepare next player");
            // Select next player and prepare it
            // If there is a clip after this one
            if ((M4OSA_UInt32)(pController->mCurrentClipNumber+1) <
             pController->mNumberClipsToPreview) {
                pController->mPrepareReqest = M4OSA_TRUE;
                pController->mCurrentPlayer++;
                if (pController->mCurrentPlayer >= kTotalNumPlayerInstances) {
                    pController->mCurrentPlayer = 0;
                }
                // Prepare the first clip to be played
                {
                    Mutex::Autolock autoLock(pController->mLockSem);
                    if (pController->mSemThreadWait != NULL) {
                        M4OSA_semaphorePost(pController->mSemThreadWait);
                    }
                }
            }
            break;
        case 0xBBBBBBBB:
        {
            ALOGV("VIDEO PLAYBACK, Update Overlay");
            int overlayIndex = ext2;
            VideoEditorCurretEditInfo *pEditInfo =
                    (VideoEditorCurretEditInfo*)M4OSA_32bitAlignedMalloc(sizeof(VideoEditorCurretEditInfo),
                    M4VS, (M4OSA_Char*)"Current Edit info");
            //ext1 = 1; start the overlay display
            //     = 2; Clear the overlay.
            pEditInfo->overlaySettingsIndex = ext2;
            pEditInfo->clipIndex = pController->mCurrentClipNumber;
            ALOGV("pController->mCurrentClipNumber = %d",pController->mCurrentClipNumber);
            if (pController->mJniCallback != NULL) {
                if (ext1 == 1) {
                    pController->mOverlayState = OVERLAY_UPDATE;
                    pController->mJniCallback(pController->mJniCookie,
                        MSG_TYPE_OVERLAY_UPDATE, pEditInfo);
                } else {
                    pController->mOverlayState = OVERLAY_CLEAR;
                    pController->mJniCallback(pController->mJniCookie,
                        MSG_TYPE_OVERLAY_CLEAR, pEditInfo);
                }
            }
            free(pEditInfo);
            break;
        }
        default:
            ALOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
            break;
    }
}

void VideoEditorPreviewController::setVideoEffectType(
    M4VSS3GPP_VideoEffectType type, M4OSA_Bool enable) {

    M4OSA_UInt32 effect = VIDEO_EFFECT_NONE;

    // map M4VSS3GPP_VideoEffectType to local enum
    switch(type) {
        case M4VSS3GPP_kVideoEffectType_FadeFromBlack:
            effect = VIDEO_EFFECT_FADEFROMBLACK;
            break;

        case M4VSS3GPP_kVideoEffectType_FadeToBlack:
            effect = VIDEO_EFFECT_FADETOBLACK;
            break;

        case M4xVSS_kVideoEffectType_BlackAndWhite:
            effect = VIDEO_EFFECT_BLACKANDWHITE;
            break;

        case M4xVSS_kVideoEffectType_Pink:
            effect = VIDEO_EFFECT_PINK;
            break;

        case M4xVSS_kVideoEffectType_Green:
            effect = VIDEO_EFFECT_GREEN;
            break;

        case M4xVSS_kVideoEffectType_Sepia:
            effect = VIDEO_EFFECT_SEPIA;
            break;

        case M4xVSS_kVideoEffectType_Negative:
            effect = VIDEO_EFFECT_NEGATIVE;
            break;

        case M4xVSS_kVideoEffectType_Framing:
            effect = VIDEO_EFFECT_FRAMING;
            break;

        case M4xVSS_kVideoEffectType_Fifties:
            effect = VIDEO_EFFECT_FIFTIES;
            break;

        case M4xVSS_kVideoEffectType_ColorRGB16:
            effect = VIDEO_EFFECT_COLOR_RGB16;
            break;

        case M4xVSS_kVideoEffectType_Gradient:
            effect = VIDEO_EFFECT_GRADIENT;
            break;

        default:
            effect = VIDEO_EFFECT_NONE;
            break;
    }

    if(enable == M4OSA_TRUE) {
        // If already set, then no need to set again
        if(!(mCurrentVideoEffect & effect))
            mCurrentVideoEffect |= effect;
            if(effect == VIDEO_EFFECT_FIFTIES) {
                mIsFiftiesEffectStarted = true;
            }
    }
    else  {
        // Reset only if already set
        if(mCurrentVideoEffect & effect)
            mCurrentVideoEffect &= ~effect;
    }

    return;
}


M4OSA_ERR VideoEditorPreviewController::applyVideoEffect(
    M4OSA_Void * dataPtr, M4OSA_UInt32 colorFormat, M4OSA_UInt32 videoWidth,
    M4OSA_UInt32 videoHeight, M4OSA_UInt32 timeMs, M4OSA_Void* outPtr) {

    M4OSA_ERR err = M4NO_ERROR;
    vePostProcessParams postProcessParams;

    postProcessParams.vidBuffer = (M4VIFI_UInt8*)dataPtr;
    postProcessParams.videoWidth = videoWidth;
    postProcessParams.videoHeight = videoHeight;
    postProcessParams.timeMs = timeMs;
    postProcessParams.timeOffset = 0; //Since timeMS already takes care of offset in this case
    postProcessParams.effectsSettings = mEffectsSettings;
    postProcessParams.numberEffects = mNumberEffects;
    postProcessParams.outVideoWidth = mOutputVideoWidth;
    postProcessParams.outVideoHeight = mOutputVideoHeight;
    postProcessParams.currentVideoEffect = mCurrentVideoEffect;
    postProcessParams.renderingMode = mRenderingMode;
    if(mIsFiftiesEffectStarted == M4OSA_TRUE) {
        postProcessParams.isFiftiesEffectStarted = M4OSA_TRUE;
        mIsFiftiesEffectStarted = M4OSA_FALSE;
    }
    else {
       postProcessParams.isFiftiesEffectStarted = M4OSA_FALSE;
    }
    //postProcessParams.renderer = mTarget;
    postProcessParams.overlayFrameRGBBuffer = NULL;
    postProcessParams.overlayFrameYUVBuffer = NULL;

    mTarget->getBufferYV12(&(postProcessParams.pOutBuffer), &(postProcessParams.outBufferStride));

    err = applyEffectsAndRenderingMode(&postProcessParams, videoWidth, videoHeight);
    return err;
}

status_t VideoEditorPreviewController::setPreviewFrameRenderingMode(
    M4xVSS_MediaRendering mode, M4VIDEOEDITING_VideoFrameSize outputVideoSize) {

    ALOGV("setMediaRenderingMode: outputVideoSize = %d", outputVideoSize);
    mRenderingMode = mode;

    status_t err = OK;
    /* get the video width and height by resolution */
    err = getVideoSizeByResolution(outputVideoSize,
              &mOutputVideoWidth, &mOutputVideoHeight);

    return err;
}

M4OSA_ERR VideoEditorPreviewController::doImageRenderingMode(
    M4OSA_Void * dataPtr, M4OSA_UInt32 colorFormat, M4OSA_UInt32 videoWidth,
    M4OSA_UInt32 videoHeight, M4OSA_Void* outPtr) {

    M4OSA_ERR err = M4NO_ERROR;
    M4VIFI_ImagePlane planeIn[3], planeOut[3];
    M4VIFI_UInt8 *inBuffer = M4OSA_NULL;
    M4OSA_UInt32 outputBufferWidth =0, outputBufferHeight=0;

    //frameSize = (videoWidth*videoHeight*3) >> 1;
    inBuffer = (M4OSA_UInt8 *)dataPtr;

    // In plane
    prepareYUV420ImagePlane(planeIn, videoWidth,
      videoHeight, (M4VIFI_UInt8 *)inBuffer, videoWidth, videoHeight);

    outputBufferWidth = mOutputVideoWidth;
    outputBufferHeight = mOutputVideoHeight;

    // Out plane
    uint8_t* outBuffer;
    size_t outBufferStride = 0;

    ALOGV("doMediaRendering CALL getBuffer()");
    mTarget->getBufferYV12(&outBuffer, &outBufferStride);

    // Set the output YUV420 plane to be compatible with YV12 format
    //In YV12 format, sizes must be even
    M4OSA_UInt32 yv12PlaneWidth = ((mOutputVideoWidth +1)>>1)<<1;
    M4OSA_UInt32 yv12PlaneHeight = ((mOutputVideoHeight+1)>>1)<<1;

    prepareYV12ImagePlane(planeOut, yv12PlaneWidth, yv12PlaneHeight,
     (M4OSA_UInt32)outBufferStride, (M4VIFI_UInt8 *)outBuffer);

    err = applyRenderingMode(planeIn, planeOut, mRenderingMode);
    if(err != M4NO_ERROR) {
        ALOGE("doImageRenderingMode: applyRenderingMode returned err=0x%x", (unsigned int)err);
    }
    return err;
}

} //namespace android