camerauis/cameraxui/cxengine/src/cxevideocapturecontrolsymbian.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 14 May 2010 15:40:46 +0300
changeset 24 2094593137f5
parent 21 fa6d9f75d6a6
child 29 699651f2666f
permissions -rw-r--r--
Revision: 201017 Kit: 201019

/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
#include <QTime>
#include <fbs.h>
#include <QPixmap>
#include <coemain.h>
#include <QStringList>
#include <AudioPreference.h>

#include "cxevideocapturecontrolsymbian.h"
#include "cxevideorecorderutilitysymbian.h"
#include "cxecameradevicecontrolsymbian.h"
#include "cxefilenamegeneratorsymbian.h"
#include "cxeerrormappingsymbian.h"
#include "cxeviewfindercontrol.h"
#include "cxestillimagesymbian.h"
#include "cxecameradevice.h"
#include "cxutils.h"
#include "cxestate.h"
#include "cxesettings.h"
#include "cxenamespace.h"
#include "cxesoundplayersymbian.h"
#include "cxequalitypresetssymbian.h"
#include "cxeviewfindercontrolsymbian.h"
#include "cxediskmonitor.h"

#include "OstTraceDefinitions.h"
#ifdef OST_TRACE_COMPILER_IN_USE
#include "cxevideocapturecontrolsymbianTraces.h"
#endif


// constants
namespace
{
    // Controller UId, can be used by the client to identify the controller, e.g. if the custom command can be used
    const TUid KCamCControllerImplementationUid = {0x101F8503};
    // TMMFEvent UIDs for Async stop
    const TUid KCamCControllerCCVideoRecordStopped = {0x2000E546};
    const TUid KCamCControllerCCVideoFileComposed = {0x2000E547};

    // Custom command for setting a new filename without closing & reopening the controller
    enum TCamCControllerCustomCommands
        {
        ECamCControllerCCNewFilename = 0,
        ECamCControllerCCVideoStopAsync
        };

    const TInt    KOneSecond                  = 1000000;
    const int     KMaintainAspectRatio        = false;
    const TInt64  KMinRequiredSpaceVideo      = 4000000;
    const uint    KOneMillion                 = 1000000;
    const qreal   KMetaDataCoeff              = 1.03;      // Coefficient to estimate metadata amount
    const uint    KCamCMaxClipDurationInSecs  = 5400;      // Maximun video clip duration in seconds
    const qreal   KCMRAvgVideoBitRateScaler   = 0.9;       // avg video bit rate scaler
}


/*!
* CxeVideoCaptureControlSymbian::CxeVideoCaptureControlSymbian
*/
CxeVideoCaptureControlSymbian::CxeVideoCaptureControlSymbian(
    CxeCameraDevice &cameraDevice,
    CxeViewfinderControl &viewfinderControl,
    CxeCameraDeviceControl &cameraDeviceControl,
    CxeFilenameGenerator &nameGenerator,
    CxeSettings &settings,
    CxeQualityPresets &qualityPresets,
    CxeDiskMonitor &diskMonitor)
    : CxeStateMachine("CxeVideoCaptureControlSymbian"),
      mVideoRecorder(NULL),
      mCameraDevice(cameraDevice),
      mCameraDeviceControl(cameraDeviceControl),
      mViewfinderControl(viewfinderControl),
      mFilenameGenerator(nameGenerator),
      mSettings(settings),
      mQualityPresets(qualityPresets),
      mDiskMonitor(diskMonitor),
      mSnapshot(),
      mNewFileName(""),
      mCurrentFilename("")
{
    CX_DEBUG_ENTER_FUNCTION();
    OstTrace0(camerax_performance, CXEVIDEOCAPTURECONTROLSYMBIAN_CREATE_IN, "msg: e_CX_VIDEOCAPTURECONTROL_NEW 1");

    qRegisterMetaType<CxeVideoCaptureControl::State> ();
    initializeStates();

    mVideoStopSoundPlayer = new
             CxeSoundPlayerSymbian(CxeSoundPlayerSymbian::VideoCaptureStop);
    mVideoStartSoundPlayer = new
             CxeSoundPlayerSymbian(CxeSoundPlayerSymbian::VideoCaptureStart);

    // If camera is already allocated, call the slot ourselves.
    if (mCameraDevice.camera()) {
        handleCameraAllocated(CxeError::None);
    }

    OstTrace0(camerax_performance, CXEVIDEOCAPTURECONTROLSYMBIAN_CREATE_M1, "msg: e_CX_ENGINE_CONNECT_SIGNALS 1");
    // connect signals from cameraDevice, so we recieve events when camera reference changes
    connect(&cameraDevice, SIGNAL(prepareForCameraDelete()),
            this, SLOT(prepareForCameraDelete()));
    connect(&cameraDevice, SIGNAL(prepareForRelease()),
            this, SLOT(prepareForRelease()) );
    connect(&cameraDevice, SIGNAL(cameraAllocated(CxeError::Id)),
            this, SLOT(handleCameraAllocated(CxeError::Id)));
    // connect playing sound signals
    connect(mVideoStartSoundPlayer, SIGNAL(playComplete(int)),
            this, SLOT(handleSoundPlayed()));

    // enabling setting change callbacks to videocapturecontrol
    connect(&mSettings, SIGNAL(settingValueChanged(const QString&,QVariant)),
            this, SLOT(handleSettingValueChanged(const QString&,QVariant)));

    connect(&mSettings, SIGNAL(sceneChanged(CxeScene&)), this, SLOT(handleSceneChanged(CxeScene&)));

    OstTrace0(camerax_performance, CXEVIDEOCAPTURECONTROLSYMBIAN_CREATE_M2, "msg: e_CX_ENGINE_CONNECT_SIGNALS 0");

    OstTrace0(camerax_performance, CXEVIDEOCAPTURECONTROLSYMBIAN_CREATE_OUT, "msg: e_CX_VIDEOCAPTURECONTROL_NEW 0");
    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* CxeVideoCaptureControlSymbian::~CxeVideoCaptureControlSymbian()
*/
CxeVideoCaptureControlSymbian::~CxeVideoCaptureControlSymbian()
{
    CX_DEBUG_ENTER_FUNCTION();

    releaseResources();
    mIcmSupportedVideoResolutions.clear();
    delete mVideoStartSoundPlayer;
    delete mVideoStopSoundPlayer;

    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* Initializes resources for video recording.
*/
void CxeVideoCaptureControlSymbian::init()
{
    CX_DEBUG_ENTER_FUNCTION();
    OstTrace0( camerax_performance, CXEVIDEOCAPTURECONTROLSYMBIAN_INIT, "msg: e_CX_VIDEO_CAPCONT_INIT 1" );

    if (state() == Idle) {
        // start initializing resources for video capture
        initVideoRecorder();
    } else if (state() == Initialized) {
        // video recorder already initalized. Continue to prepare video reocording.
        open();
    }

    OstTrace0( camerax_performance, DUP1_CXEVIDEOCAPTURECONTROLSYMBIAN_INIT, "msg: e_CX_VIDEO_CAPCONT_INIT 0" );

    CX_DEBUG_EXIT_FUNCTION();
}


/*
* Releases all resources
*/
void CxeVideoCaptureControlSymbian::deinit()
{
    CX_DEBUG_ENTER_FUNCTION();

    if(state() == Idle) {
        // nothing to do
        return;
    }

    OstTrace0( camerax_performance, CXEVIDEOCAPTURECONTROLSYMBIAN_DEINIT, "msg: e_CX_VIDEO_CAPCONT_DEINIT 1" );

    // first stop viewfinder
    mViewfinderControl.stop();

    // stop video-recording in-case if its ongoing.
    stop();

    if (mCameraDevice.cameraSnapshot()) {
        mCameraDevice.cameraSnapshot()->StopSnapshot();
    }

    if (mVideoRecorder) {
        mVideoRecorder->Close();
    }

    // revert back the new filename to empty string so that we generate a new file name
    // when we init again
    mNewFileName = QString("");

    setState(Idle);

    OstTrace0( camerax_performance, DUP1_CXEVIDEOCAPTURECONTROLSYMBIAN_DEINIT, "msg: e_CX_VIDEO_CAPCONT_DEINIT 0" );

    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* Intializes VideoRecorder for recording.
*/
void CxeVideoCaptureControlSymbian::initVideoRecorder()
{
    CX_DEBUG_ENTER_FUNCTION();

    if (state() != Idle) {
        // not valid state to start "open" operation
        return;
    }

    if(!mVideoRecorder) {
        // if video recorder is not created, do it now.
        createVideoRecorder();
    }

    // update current video quality details from icm
    CxeError::Id err = getVideoQualityDetails(mCurrentVideoDetails);

    if (!err) {
        // read videofile mime type
        QByteArray videoFileData =
                    mCurrentVideoDetails.mVideoFileMimeType.toLatin1();
        TPtrC8 videoFileMimeType(reinterpret_cast<const TUint8*>
                           (videoFileData.constData()), videoFileData.size());

        // read preferred supplier
        TPtrC16 supplier(reinterpret_cast<const TUint16*>
                           (mCurrentVideoDetails.mPreferredSupplier.utf16()));

        err = findVideoController(videoFileMimeType, supplier);

        if (!err) {
            // video recorder is ready to open video file for recording.
            setState(Initialized);
            open();
        }
    } else {
        err = CxeErrorHandlingSymbian::map(KErrNotReady);
    }

    if (err) {
        // In case of error
        emit videoPrepareComplete(err);
        deinit();
    }

    CX_DEBUG_EXIT_FUNCTION();
}



/*!
* Opens file for video recording.
*/
void CxeVideoCaptureControlSymbian::open()
{
    CX_DEBUG_ENTER_FUNCTION();

    if (state() != Initialized) {
        // not valid state to start "open" operation
        return;
    }

    CxeError::Id err = CxeError::None;

    // generate video file name, if necessary
    if (mNewFileName.isEmpty()) {
        QStringList list = mCurrentVideoDetails.mVideoFileMimeType.split("/");
        QString fileExt(".");
        if (list.count() == 2) {
            fileExt = fileExt + list[1];
        }
        // Generate new filename and open the file for writing video data
        err = mFilenameGenerator.generateFilename(mNewFileName, fileExt);
        if (err == CxeError::None) {
            mCurrentFilename = mNewFileName;
        } else {
            // file name is not valid, re-initialize the value of current string
            // back to empty string
            mCurrentFilename = QString("");
        }
    }

    if (!err &&
        mVideoRecorder &&
        !mCurrentFilename.isEmpty()) {

        TPtrC16 fName(reinterpret_cast<const TUint16*>(mCurrentFilename.utf16()));
        CX_DEBUG(( "Next video file path: %s", mCurrentFilename.toAscii().constData() ));

        // read video codec mime type
        QByteArray videoCodecData =
                    mCurrentVideoDetails.mVideoCodecMimeType.toLatin1();
        TPtrC8 videoCodecMimeType(reinterpret_cast<const TUint8*>
                           (videoCodecData.constData()), videoCodecData.size());

        setState(CxeVideoCaptureControl::Preparing);

        TRAPD(openErr, mVideoRecorder->OpenFileL(fName,
                       mCameraDevice.camera()->Handle(),
                       mVideoControllerUid,
                       mVideoFormatUid,
                       videoCodecMimeType,
                       audioType(mCurrentVideoDetails.mAudioType)));

        err = CxeErrorHandlingSymbian::map(openErr);
    }
    if (err) {
        // error occured.
        deinit();
        emit videoPrepareComplete(err);
    }
    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* Prepare Video Recorder with necessary settings for video capture.
*/
TFourCC CxeVideoCaptureControlSymbian::audioType(const QString& str)
{
    CX_DEBUG_ENTER_FUNCTION();

    QByteArray audioType = str.toAscii();

    quint8 char1(' ');
    quint8 char2(' ');
    quint8 char3(' ');
    quint8 char4(' ');

    if (audioType.count() > 3) {
        char1 = audioType[0];
        char2 = audioType[1];
        char3 = audioType[2];

        if (audioType.count() == 4) {
            char4 = audioType[3];
        }
    }

    return TFourCC(char1, char2, char3, char4);
}


/*!
* Prepare Video Recorder with necessary settings for video capture.
*/
void CxeVideoCaptureControlSymbian::prepare()
{
    CX_DEBUG_ENTER_FUNCTION();

    if (state() != Preparing) {
        // not valid state to continue prepare.
        return;
    }

    CX_DEBUG(("Video resoulution (%d,%d)", mCurrentVideoDetails.mWidth,
                                           mCurrentVideoDetails.mHeight));
    CX_DEBUG(("Video bitrate = %d)", mCurrentVideoDetails.mVideoBitRate));

    OstTrace0(camerax_performance, CXEVIDEOCAPTURECONTROLSYMBIAN_PREPARE, "msg: e_CX_VIDCAPCONT_PREPARE 1");
    TSize frameSize;
    frameSize.SetSize(mCurrentVideoDetails.mWidth, mCurrentVideoDetails.mHeight);

    int muteSetting = 0; // audio enabled
    mSettings.get(CxeSettingIds::VIDEO_MUTE_SETTING, muteSetting);

    // Check if scene defines frame rate.
    // Use generic frame rate defined in video details, if no value is set in scene.
    int frameRate = 0;
    mSettings.get(CxeSettingIds::FRAME_RATE, frameRate);
    if (frameRate <= 0) {
        frameRate = mCurrentVideoDetails.mVideoFrameRate;
    }

    CX_DEBUG(("Video frame rate = %d)", frameRate));

    TRAPD(err,
              {
              mVideoRecorder->SetVideoFrameSizeL(frameSize);
              mVideoRecorder->SetVideoFrameRateL(frameRate);
              mVideoRecorder->SetVideoBitRateL(mCurrentVideoDetails.mVideoBitRate);
              mVideoRecorder->SetAudioEnabledL(muteSetting == 0);
              // "No limit" value is handled in video recorder wrapper.
              mVideoRecorder->SetMaxClipSizeL(mCurrentVideoDetails.mMaximumSizeInBytes);
              }
         );

    if (!err) {
        // settings have been applied successfully, start to prepare
        mVideoRecorder->Prepare();
        // prepare snapshot
        err = prepareVideoSnapshot();
    }

    if (!err) {
        // prepare zoom only when there are no errors during prepare.
        emit prepareZoomForVideo();
    }
    // emit video prepare status
    emit videoPrepareComplete(CxeErrorHandlingSymbian::map(err));

    OstTrace0(camerax_performance, DUP1_CXEVIDEOCAPTURECONTROLSYMBIAN_PREPARE, "msg: e_CX_VIDCAPCONT_PREPARE 0");

    CX_DEBUG_EXIT_FUNCTION();
}



/*!
Fetches video qualites details based on video quality setting.
Returns CxeError codes if any.
*/
CxeError::Id
CxeVideoCaptureControlSymbian::getVideoQualityDetails(CxeVideoDetails &videoInfo)
{
    CX_DEBUG_ENTER_FUNCTION();

    int videoQuality = 0;
    CxeError::Id err = CxeError::None;

    if (mCameraDeviceControl.cameraIndex() == Cxe::PrimaryCameraIndex) {
        err = mSettings.get(CxeSettingIds::VIDEO_QUALITY, videoQuality);

        bool validQuality = (videoQuality >= 0 &&
                             videoQuality < mIcmSupportedVideoResolutions.count());
        if (err == CxeError::None && validQuality) {
            // get video quality details
            videoInfo = mIcmSupportedVideoResolutions.at(videoQuality);
        } else {
            // not valid video quality
            err = CxeError::NotFound;
        }
    } else {
        // get secondary camera video quality index
        if (mIcmSupportedVideoResolutions.count() > 0) {
            videoInfo = mIcmSupportedVideoResolutions.at(videoQuality);
        } else {
            // not valid video quality
            err = CxeError::NotFound;
        }
    }

    CX_DEBUG_EXIT_FUNCTION();

    return err;
}


/**!
 Prepare snapshot
 Returns symbian error code.
 */
int CxeVideoCaptureControlSymbian::prepareVideoSnapshot()
{
    CX_DEBUG_ENTER_FUNCTION();

    CCamera::CCameraSnapshot *cameraSnapshot = mCameraDevice.cameraSnapshot();
    CX_ASSERT_ALWAYS(cameraSnapshot);

    int err = KErrNone;
    // Whether or not we have postcapture on, we need the snapshot for Thumbnail Manager.
    if (cameraSnapshot) {
        // Cancel active snapshot
        cameraSnapshot->StopSnapshot();

        // Prepare snapshot
        CCamera::TFormat snapFormat = CCamera::EFormatFbsBitmapColor16MU;
        TRAP(err, cameraSnapshot->PrepareSnapshotL(snapFormat,
                                                   getSnapshotSize(),
                                                   KMaintainAspectRatio));
        CX_DEBUG(("PrepareSnapshotL done, err=%d", err));
        // Start snapshot if no errors encountered.
        if (err == KErrNone) {
            CX_DEBUG(("Start video snapshot"));
            cameraSnapshot->StartSnapshot();
        }
    } else {
        // No snapshot interface available. Report error.
        // Assert above takes care of this, but keeping this as an option.
        err = KErrNotReady;
    }

    CX_DEBUG_EXIT_FUNCTION();

    return err;
}



/*!
* Returns snapshot size. Snapshot size is calculated based on the
* display resolution and current video aspect ratio.
*/
TSize CxeVideoCaptureControlSymbian::getSnapshotSize() const
{
    CX_DEBUG_ENTER_FUNCTION();

    TSize snapshotSize;

    QSize deviceResolution = mViewfinderControl.deviceDisplayResolution();
    QSize size = QSize(mCurrentVideoDetails.mWidth, mCurrentVideoDetails.mHeight);

    // scale according to aspect ratio.
    size.scale(deviceResolution.width(),
               deviceResolution.height(),
               Qt::KeepAspectRatio);
    CX_DEBUG(("Video Snapshot size, (%d,%d)", size.width(), size.height()));
    snapshotSize.SetSize(size.width(), deviceResolution.height());

    CX_DEBUG_EXIT_FUNCTION();

    return snapshotSize;
}



/**!
* Camera events coming from ecam.
*/
void CxeVideoCaptureControlSymbian::handleCameraEvent(int eventUid, int error)
{
    CX_DEBUG_ENTER_FUNCTION();
    if (eventUid == KUidECamEventSnapshotUidValue) {
        handleSnapshotEvent(CxeErrorHandlingSymbian::map(error));
    }
    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* Handle Snapshot event from ecam
*/
void CxeVideoCaptureControlSymbian::handleSnapshotEvent(CxeError::Id error)
{
    CX_DEBUG_ENTER_FUNCTION();

    if (state() == Idle) {
        // we ignore this event, when we are not in active state(s)
        CX_DEBUG(( "wrong state, ignoring snapshot" ));
        CX_DEBUG_EXIT_FUNCTION();
        return;
    }

    if (error) {
        mSnapshot = QPixmap();
        emit snapshotReady(error, mSnapshot, filename());
        return;
    }

    RArray<TInt> snapList;
    MCameraBuffer* buffer(NULL);
    // Note: Cleanup not required in this function
    CFbsBitmap *snapshot = NULL;
    TRAPD(snapErr,
          buffer = &mCameraDevice.cameraSnapshot()->SnapshotDataL(snapList));
    if (!snapErr) {
        TInt firstImageIndex = snapList.Find(0);
        snapList.Close();
        TRAP(snapErr, snapshot = &(buffer->BitmapL(firstImageIndex)));
    }
    if (!snapErr) {
        TSize size = snapshot->SizeInPixels();
        TInt sizeInWords = size.iHeight * CFbsBitmap::ScanLineLength(size.iWidth, EColor16MU)
                / sizeof(TUint32);

        TUint32 *snapshotData = NULL;
        snapshotData = new TUint32[sizeInWords];

        if (snapshotData) {
            // Convert to QImage
            snapshot->LockHeap();
            TUint32* dataPtr = snapshot->DataAddress();
            memcpy(snapshotData, dataPtr, sizeof(TUint32) * sizeInWords);
            snapshot->UnlockHeap();


            QImage *img = new QImage((uchar*) snapshotData, size.iWidth, size.iHeight,
                                   CFbsBitmap::ScanLineLength(size.iWidth, EColor16MU),
                                   QImage::Format_RGB32);

            mSnapshot = QPixmap::fromImage(*img);
            delete [] snapshotData;
            delete img;
        }
    }
    if (buffer) {
        buffer->Release();
    }
    // Snapshot ready
    emit snapshotReady(CxeErrorHandlingSymbian::map(snapErr), mSnapshot, filename());

    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* Resets the video snapshot and current video filename
*/
void CxeVideoCaptureControlSymbian::reset()
{
    CX_DEBUG_ENTER_FUNCTION();

    // Snapshot will consume considerably memory.
    // Replace it with null pixmap to have it freed.
    mSnapshot = QPixmap();
    // reset the current file name.
    mCurrentFilename = QString("");

    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* Returns current video filename
*/
QString CxeVideoCaptureControlSymbian::filename() const
{
    // Simply return the current contents of mCurrentFilename.
    // If video recording was started then it returns proper filename
    // otherwise an empty string is returned.
    return mCurrentFilename;
}


/*!
* Returns current video snapshot
*/
QPixmap CxeVideoCaptureControlSymbian::snapshot() const
{
    return mSnapshot;
}


/*!
* Starts video recording if we are in appropriate state.
*/
void CxeVideoCaptureControlSymbian::record()
{
    CX_DEBUG_ENTER_FUNCTION();

    if (state() == Ready || state() == Paused) {
        // ask the player to play the sound
        // recording will start once start sound is played
        setState(CxeVideoCaptureControl::PlayingStartSound);
        mVideoStartSoundPlayer->play();
    }

    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* Pauses video recording.
*/
void CxeVideoCaptureControlSymbian::pause()
{
    CX_DEBUG_ENTER_FUNCTION();

    setState(CxeVideoCaptureControl::Paused);
    TRAPD(pauseErr, mVideoRecorder->PauseL());
    // play the sound, but not changing the state
    mVideoStopSoundPlayer->play();
    if (pauseErr) {
        CX_DEBUG(("[WARNING] Error %d pausing!", pauseErr));
        //pause operation failed, report it
        emit videoComposed(CxeErrorHandlingSymbian::map(pauseErr), filename());
        // release resources.
        deinit();
    }

    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* Stops video recording.
*/
void CxeVideoCaptureControlSymbian::stop()
{
    CX_DEBUG_ENTER_FUNCTION();

    if (state() == Recording || state() == Paused) {
        // first stop viewfinder
        mViewfinderControl.stop();

        TInt asyncStopErr = mVideoRecorder->CustomCommandSync(
            TMMFMessageDestination(KCamCControllerImplementationUid,
                                   KMMFObjectHandleController),
            ECamCControllerCCVideoStopAsync,
            KNullDesC8,
            KNullDesC8 );
        CX_DEBUG(("ECamCControllerCCVideoStopAsync sent, err=%d", asyncStopErr));
        if (asyncStopErr) { // fall back to synchronous stop
            TInt syncStopErr = mVideoRecorder->Stop();
            if (syncStopErr) {
                // error during stop operation, release resources
                emit videoComposed(CxeErrorHandlingSymbian::map(asyncStopErr), filename());
                deinit();
            } else {
                // stop operation went fine, set back the state to Initialized.
                setState(Initialized);
                mFilenameGenerator.raiseCounterValue();
            }
        } else {
            // No error from asynch stop custom command... wait for stop event
            setState(Stopping);
            mFilenameGenerator.raiseCounterValue();
        }
    }

    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* Callback when "Open" operation is complete.
*/
void CxeVideoCaptureControlSymbian::MvruoOpenComplete(TInt aError)
{
    CX_DEBUG_ENTER_FUNCTION();
    CX_DEBUG(("CxeVideoCaptureControlSymbian::MvruoOpenComplete, err=%d", aError));

    if (state() == Preparing) {
        if (aError != KErrNone) {
            deinit();
            CxeError::Id err = CxeErrorHandlingSymbian::map(KErrNotReady);
            // report error to interested parties
            emit videoPrepareComplete(CxeErrorHandlingSymbian::map(err));
        } else {
            prepare();
        }
    }

    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* Callback when "Prepare" request is complete.
*/
void CxeVideoCaptureControlSymbian::MvruoPrepareComplete(TInt aError)
{
    CX_DEBUG_ENTER_FUNCTION();
    CX_DEBUG(("CxeVideoCaptureControlSymbian::MvruoPrepareComplete, err=%d", aError));

    if (state() == Preparing) {
        if (!aError) {
            setState(CxeVideoCaptureControl::Ready);
            mViewfinderControl.start();
            OstTrace0( camerax_performance, CXEVIDEOCAPTURECONTROLSYMBIAN_GOTOVIDEO, "msg: e_CX_GO_TO_VIDEO_MODE 0" );
        } else {
            deinit();
            // report error to interested parties
            CxeError::Id err = CxeErrorHandlingSymbian::map(KErrNotReady);
            emit videoPrepareComplete(CxeErrorHandlingSymbian::map(err));
        }
    }

    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* Callback when "Record" operation is complete.
*/
void CxeVideoCaptureControlSymbian::MvruoRecordComplete(TInt aError)
{
    CX_DEBUG_ENTER_FUNCTION();
    CX_DEBUG(("CxeVideoCaptureControlSymbian::MvruoRecordComplete, err=%d", aError));

    //! async stop customcommand stuff
    if (aError == KErrNone) {
        setState(CxeVideoCaptureControl::Ready);
    } else if (aError == KErrCompletion) {
        // KErrCompletion is received when video recording stops
        // because of maximum clip size is reached. Emulate
        // normal stopping.
        setState(Stopping);
        MvruoEvent(TMMFEvent(KCamCControllerCCVideoRecordStopped, KErrNone));
        MvruoEvent(TMMFEvent(KCamCControllerCCVideoFileComposed, KErrNone));
    }
    else {
        // error during recording, report to client
        deinit();
        emit videoComposed(CxeErrorHandlingSymbian::map(aError), filename());
    }

    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* Callback from MVideoRecorderUtilityObserver
*/
void CxeVideoCaptureControlSymbian::MvruoEvent(const TMMFEvent& aEvent)
{
    CX_DEBUG_ENTER_FUNCTION();

    if (aEvent.iEventType.iUid == KCamCControllerCCVideoRecordStopped.iUid) {
        CX_DEBUG(("KCamCControllerCCVideoRecordStopped"));
        // play the sound, but not changing the state
        mVideoStopSoundPlayer->play();
    } else if (aEvent.iEventType.iUid == KCamCControllerCCVideoFileComposed.iUid) {
        CX_DEBUG(("KCamCControllerCCVideoFileComposed"));
        if (state() == Stopping) {
            // stop operation went fine, set back the state to intialized.
            setState(Initialized);
        }
        // video file has composed, everything went well, inform the client
        emit videoComposed(CxeError::None, filename());
        // revert back the new filename to empty string, since recording
        // is complete and we need to generate a new file name
        mNewFileName = QString("");
    } else {
        CX_DEBUG(("ignoring unknown MvruoEvent 0x%08x", aEvent.iEventType.iUid ));
    }

    CX_DEBUG_EXIT_FUNCTION();
}


/*!
Get corresponding controller for video capture.
@param aMimeType denotes videofile mimetype,
@param aPreferredSupplier denotes supplier.
Returns CxeError::Id if any.
*/
CxeError::Id
CxeVideoCaptureControlSymbian::findVideoController(const TDesC8& aMimeType,
                                                   const TDesC& aSupplier)
{
    CX_DEBUG_ENTER_FUNCTION();

    CX_DEBUG(("video file mime type : %s", &aMimeType));
    CX_DEBUG(("supplier name: %s", &aSupplier));

    mVideoControllerUid.iUid = 0;
    mVideoFormatUid.iUid = 0;

    // Retrieve a list of possible controllers from ECOM.
    // Controller must support recording the requested mime type.
    // Controller must be provided by preferred supplier.

    CMMFControllerPluginSelectionParameters* cSelect(NULL);
    CMMFFormatSelectionParameters* fSelect(NULL);
    RMMFControllerImplInfoArray controllers;

    TRAPD(err, cSelect = CMMFControllerPluginSelectionParameters::NewL());
    if (err) {
        return CxeErrorHandlingSymbian::map(err);
    }

    TRAP(err, fSelect = CMMFFormatSelectionParameters::NewL());
    if (err) {
        if(cSelect) {
            delete cSelect;
        }
        return CxeErrorHandlingSymbian::map(err);
    }

    TRAP( err, {
        fSelect->SetMatchToMimeTypeL(aMimeType);
        cSelect->SetRequiredRecordFormatSupportL(*fSelect);
        cSelect->SetPreferredSupplierL(aSupplier,
                                       CMMFPluginSelectionParameters::EOnlyPreferredSupplierPluginsReturned);
        cSelect->ListImplementationsL(controllers);
    } );

    if (!err && controllers.Count() >= 1) {
        // KErrNotFound is returned unless a controller is found
        err = KErrNotFound;
        // Get the controller UID.
        mVideoControllerUid = controllers[0]->Uid();

        // Inquires the controller about supported formats.
        // We use the first controller found having index 0.
        RMMFFormatImplInfoArray formats;
        formats = controllers[0]->RecordFormats();

        // Get the first format that supports our mime type.
        int count = formats.Count();
        for (int i=0; i<count; i++) {
            if (formats[i]->SupportsMimeType(aMimeType)) {
                mVideoFormatUid = formats[i]->Uid(); // set the UID
                err = KErrNone;
                break;
            }
        }
    }
    delete cSelect;
    delete fSelect;
    controllers.ResetAndDestroy();

    CX_DEBUG_EXIT_FUNCTION();

    return CxeErrorHandlingSymbian::map(err);
}


/*!
* camera reference changing, release resources
*/
void CxeVideoCaptureControlSymbian::prepareForCameraDelete()
{
    CX_DEBUG_ENTER_FUNCTION();
    releaseResources();
    CX_DEBUG_EXIT_FUNCTION();
}

/*!
* prepare for camera release.
*/
void CxeVideoCaptureControlSymbian::prepareForRelease()
{
    CX_DEBUG_ENTER_FUNCTION();
    deinit();
    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* new camera available,
*/
void CxeVideoCaptureControlSymbian::handleCameraAllocated(CxeError::Id error)
{
    CX_DEBUG_ENTER_FUNCTION();

    if (!error) {
        // initialize the video recorder utility
        createVideoRecorder();
        // new camera available, read supported video qualities from icm
        // load all still qualities supported by icm
        mIcmSupportedVideoResolutions.clear();
        Cxe::CameraIndex cameraIndex = mCameraDeviceControl.cameraIndex();
        // get list of supported image qualities based on camera index
        mIcmSupportedVideoResolutions =
                mQualityPresets.videoQualityPresets(cameraIndex);
    }

    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* Initializes video recorder.
*/
void CxeVideoCaptureControlSymbian::createVideoRecorder()
{
    CX_DEBUG_ENTER_FUNCTION();

    // init video recoder
    if (state() == CxeVideoCaptureControl::Idle) {
        if (mVideoRecorder == NULL) {
            TRAPD(initErr, mVideoRecorder =
                new CxeVideoRecorderUtilitySymbian( *this ,
                            KAudioPriorityVideoRecording,
                            TMdaPriorityPreference( KAudioPrefVideoRecording )));
            if (initErr) {
                CX_DEBUG(("WARNING - VideoRecorderUtility could not be reserved. Failed with err:%d", initErr));
                mVideoRecorder = NULL;
            }
        }
    }

    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* releases resources used by videocapture
*/
void CxeVideoCaptureControlSymbian::releaseResources()
{
    CX_DEBUG_ENTER_FUNCTION();

    // Save the state and filename before releasing.
    QString filenameCopy(filename());
    CxeVideoCaptureControl::State stateCopy(state());

    // first de-init videocapture control
    deinit();
    reset();

    // Check if state is stopping, in which case we have to inform the
    // file harvester that a file is to be completed. We would not
    // call harvestFile otherwise in this case.
    // Otherwise the video will not be found from videos app.
    if (stateCopy == CxeVideoCaptureControl::Stopping) {
        emit videoComposed(CxeError::None, filenameCopy);
    }

    delete mVideoRecorder;
    mVideoRecorder = NULL;

    CX_DEBUG_EXIT_FUNCTION();
}


/*!
Returns current state of videocapture
*/
CxeVideoCaptureControl::State CxeVideoCaptureControlSymbian::state() const
{
    return static_cast<CxeVideoCaptureControl::State> (stateId());
}


/*!
* slot called when state is changed.
*/
void CxeVideoCaptureControlSymbian::handleStateChanged(int newStateId, CxeError::Id error)
{
    switch (newStateId) {
    case Ready:
        if (error == CxeError::None && !mDiskMonitor.isMonitoring()) {
            mDiskMonitor.start();
            connect(&mDiskMonitor, SIGNAL(diskSpaceChanged()), this, SLOT(handleDiskSpaceChanged()));
        }
        break;
    default:
        // Stop monitoring when video mode is released.
        // Same goes during recording, as video times come from recorder.
        if (mDiskMonitor.isMonitoring()) {
            mDiskMonitor.stop();
            disconnect(&mDiskMonitor, SIGNAL(diskSpaceChanged()), this, SLOT(handleDiskSpaceChanged()));
        }
        break;
    }
    emit stateChanged(static_cast<State> (newStateId), error);
}


/*!
* Initialize states for videocapturecontrol
*/
void CxeVideoCaptureControlSymbian::initializeStates()
{
    // addState( id, name, allowed next states )
    addState(new CxeState(Idle, "Idle", Initialized));
    addState(new CxeState(Initialized, "Initialized", Preparing | Idle));
    addState(new CxeState(Preparing, "Preparing", Ready | Idle));
    addState(new CxeState(Ready, "Ready", Recording | PlayingStartSound | Preparing | Idle));
    addState(new CxeState(Recording, "Recording", Recording | Paused | Stopping | Idle));
    addState(new CxeState(Paused, "Paused", Recording | Stopping | PlayingStartSound | Idle));
    addState(new CxeState(Stopping, "Stopping", Initialized | Idle));
    addState(new CxeState(PlayingStartSound, "PlayingStartSound", Recording | Idle));

    setInitialState(Idle);
}


/*!
* Updates remaining video recordng time counter to all the video qualities supported by ICM
* this should be done whenever storage location setting changes and when values are
* read from ICM for the first time
*/
void CxeVideoCaptureControlSymbian::updateRemainingRecordingTimeCounter()
{
    CX_DEBUG_ENTER_FUNCTION();

    for( int index = 0; index < mIcmSupportedVideoResolutions.count(); index++) {
        CxeVideoDetails &qualityDetails = mIcmSupportedVideoResolutions[index];
        calculateRemainingTime(qualityDetails, qualityDetails.mRemainingTime);
    }

    CX_DEBUG_EXIT_FUNCTION();
}

/*
* calculates remaining video recording time.
*/
void CxeVideoCaptureControlSymbian::remainingTime(int &time)
{
    CX_DEBUG_ENTER_FUNCTION();

    if (state() == CxeVideoCaptureControl::Recording ||
        state() == CxeVideoCaptureControl::Paused) {
        TTimeIntervalMicroSeconds remaining = 0;
        remaining = mVideoRecorder->RecordTimeAvailable();
        time = remaining.Int64() * 1.0 / KOneSecond;
        CX_DEBUG(( "timeRemaining2: %d", time ));
    } else {
        // Check if we need to recalculate the remaining time.
        if (mCurrentVideoDetails.mRemainingTime == CxeVideoDetails::UNKNOWN) {
            calculateRemainingTime(mCurrentVideoDetails, mCurrentVideoDetails.mRemainingTime);
        }
        time = mCurrentVideoDetails.mRemainingTime;
    }

    CX_DEBUG_EXIT_FUNCTION();
}



/*!
* algorithm to calculate remaining recording time
@ param videoDetails contains the current video resolution that is in use.
@ time contains the remaining recording time
*/
void CxeVideoCaptureControlSymbian::calculateRemainingTime(CxeVideoDetails videoDetails, int &time)
{
    CX_DEBUG_ENTER_FUNCTION();

    TTimeIntervalMicroSeconds remaining = 0;

    // get available space in the drive selected in the settings
    // for storing videos
    qint64 availableSpace = mDiskMonitor.free();

    availableSpace = availableSpace - KMinRequiredSpaceVideo;

    // Maximum clip size may be limited for mms quality.
    // If mMaximumSizeInBytes == 0, no limit is specified.
    if (videoDetails.mMaximumSizeInBytes > 0 && videoDetails.mMaximumSizeInBytes < availableSpace) {
        availableSpace = videoDetails.mMaximumSizeInBytes;
    }

    // Use average audio/video bitrates to estimate remaining time
    quint32  averageBitRate = 0;
    quint32  averageByteRate = 0;
    qreal    scaler = mQualityPresets.avgVideoBitRateScaler();

    if (scaler == 0) {
        // video bit rate scaler is 0, use the constant value
        scaler = KCMRAvgVideoBitRateScaler;
    }

    int avgVideoBitRate = (videoDetails.mVideoBitRate * scaler);
    int avgAudioBitRate =  videoDetails.mAudioBitRate;

    int muteSetting = 0; // audio enabled
    mSettings.get(CxeSettingIds::VIDEO_MUTE_SETTING, muteSetting);

    if (muteSetting == 1) {
        // audio disabled from setting. hence no audio
        avgAudioBitRate = 0;
    }

    averageBitRate = (quint32)(
                     (avgVideoBitRate + avgAudioBitRate) * KMetaDataCoeff);

    averageByteRate = averageBitRate / 8;

    if (availableSpace <= 0) {
        remaining = 0;
    } else {
        // converting microseconds to seconds
        remaining = availableSpace * KOneMillion / averageByteRate;
        if ( (remaining.Int64()) > (quint64(KCamCMaxClipDurationInSecs) * KOneMillion) ) {
            remaining = (quint64(KCamCMaxClipDurationInSecs) * KOneMillion);
        }
    }
    if ( remaining <= quint64(0) ) {
        remaining = 0;
    }

    time = remaining.Int64() * 1.0 / KOneSecond;

    CX_DEBUG(( "remaining time from algorithm: %d", time ));

    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* Calculates remaining recording time during video recording
*/
bool CxeVideoCaptureControlSymbian::elapsedTime(int &time)
{
    CX_DEBUG_ENTER_FUNCTION();

    TTimeIntervalMicroSeconds timeElapsed = 0;
    bool ok = false;
    if (state() == CxeVideoCaptureControl::Recording ||
        state() == CxeVideoCaptureControl::Paused) {
        TRAPD( err, timeElapsed = mVideoRecorder->DurationL() );
        if (!err) {
            time = timeElapsed.Int64() * 1.0 / KOneSecond;
            CX_DEBUG(("timeElapsed2: %d", time));
            ok = true;
        }
    }

    CX_DEBUG_EXIT_FUNCTION();

    return ok;
}

/*!
* slot called when playing a sound has finished.
*/
void CxeVideoCaptureControlSymbian::handleSoundPlayed()
{
    CX_DEBUG_ENTER_FUNCTION();

    // start recording, if we were playing capture sound
    if (state() == CxeVideoCaptureControl::PlayingStartSound) {
        setState(CxeVideoCaptureControl::Recording);

        mVideoRecorder->Record();
    }

    // in case of video capture stop sound playing, nothing needs to be done
    // meaning the state set elsewhere, and the video capture has been stopped already

    CX_DEBUG_EXIT_FUNCTION();
}


/*!
* setting has changed, check if we are interested.
*/
void CxeVideoCaptureControlSymbian::handleSettingValueChanged(const QString& settingId,
                                                              QVariant newValue)
{
    CX_DEBUG_ENTER_FUNCTION();
    Q_UNUSED(newValue);

    if (mCameraDeviceControl.mode() == Cxe::VideoMode) {
        if (settingId == CxeSettingIds::VIDEO_QUALITY) {
            // re-prepare for video
            if (state() == Ready) {
                // release resources
                deinit();
                // initialize video recording again
                init();
            }
        } else if (settingId == CxeSettingIds::VIDEO_MUTE_SETTING) {
            // mute setting changed, apply the new setting and re-prepare.
            setState(Preparing);
            prepare();
        } else if (settingId == CxeSettingIds::FRAME_RATE){
            // Frame rate setting changed. Need to re-prepare if we are prepared already.
            // Otherwise can wait for next init call.
            if (state() == Ready) {
                setState(Preparing);
                prepare();
            }
        } else {
            // Setting not relevant to video mode
        }
    }

    CX_DEBUG_EXIT_FUNCTION();
}

/*!
 * Scene mode changed. We need to know about it because frame rate
 * might have changed.
 */
void CxeVideoCaptureControlSymbian::handleSceneChanged(CxeScene& scene)
{
    CX_DEBUG_ENTER_FUNCTION();

    // make sure we are in video mode
    if (mCameraDeviceControl.mode() == Cxe::VideoMode) {
        // Frame rate setting might have changed so re-prepare.
        if (state() == Ready) {
            setState(Preparing);
            prepare();
        }

    }
    CX_DEBUG_EXIT_FUNCTION();
}

/*!
* Disk space changed.
* Emit remaining time changed signal, if space change affects it.
*/
void CxeVideoCaptureControlSymbian::handleDiskSpaceChanged()
{
    CX_DEBUG_ENTER_FUNCTION();

    // Ignore updates on preparing phase.
    if (state() == CxeVideoCaptureControl::Ready) {

        int time(0);
        calculateRemainingTime(mCurrentVideoDetails, time);

        if (time !=  mCurrentVideoDetails.mRemainingTime) {
            mCurrentVideoDetails.mRemainingTime = time;
            emit remainingTimeChanged();
        }
    }

    CX_DEBUG_EXIT_FUNCTION();
}

/*!
* Returns QList of all supported video quality details based on the camera index
* (primary/secondary).
*/
QList<CxeVideoDetails> CxeVideoCaptureControlSymbian::supportedVideoQualities()
{
    // update the remaining time counters for each quality setting
    updateRemainingRecordingTimeCounter();
    return mIcmSupportedVideoResolutions;
}

// End of file