videoeditorengine/vedengine/videoprocessor/src/VideoEncoderMDF.cpp
author Mikael Laine <mikael.laine@ixonos.com>
Fri, 29 Jan 2010 14:08:33 +0200
changeset 0 951a5db380a0
permissions -rw-r--r--
Committing the Video Editor package under the Eclipse Public License

/*
* Copyright (c) 2010 Ixonos Plc.
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the "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:
* Ixonos Plc
*
* Description:  
* Implementation for video encoder.
*
*/


#include "statusmonitor.h"
#include "videoencoder.h"
#include "vedvideosettings.h"
#include "vedvolreader.h"
#include "vedavcedit.h"


// Assertion macro wrapper for code cleanup
#define VEASSERT(x) __ASSERT_DEBUG(x, User::Panic(_L("CVIDEOENCODER"), EInternalAssertionFailure)) 

// Debug print macro
#ifdef _DEBUG
#include <e32svr.h>
#define PRINT(x) RDebug::Print x
#else
#define PRINT(x)
#endif

// Constants


// time increment resolution
const TUint KTimeIncrementResolutionHW = 30000;
//const TUint KTimeIncrementResolutionSW = 29;

const TReal KMinRandomAccessRate = 0.2;
//const TUint KPictureQuality = 50;
//const TReal KLatencyQyalityTradeoff = 1.0;
//const TReal KQualityTemporalTradeoff = 0.8;
//const TBool KEncodingRealTime = EFalse;


// ================= MEMBER FUNCTIONS =======================

// ---------------------------------------------------------
// CVideoEncoder::NewL
// Two-phased constructor.
// ---------------------------------------------------------
//

CVideoEncoder* CVideoEncoder::NewL(CStatusMonitor *aMonitor, CVedAVCEdit* aAvcEdit, 
                                   const TPtrC8& aVideoMimeType)
{
	PRINT((_L("CVideoEncoder::NewL() : MDF"))); 
	
	CVideoEncoder* self = new (ELeave) CVideoEncoder();    
	CleanupStack::PushL( self );
	self->ConstructL(aMonitor, aAvcEdit, aVideoMimeType);
	CleanupStack::Pop();
	
	return self;    
}


CVideoEncoder::CVideoEncoder() : CActive(EPriorityStandard), iInputBuffer(0,0)
{    
    iPreviousTimeStamp = TTimeIntervalMicroSeconds(0);
    iKeyFrame = EFalse;
}


void CVideoEncoder::ConstructL(CStatusMonitor *aMonitor, CVedAVCEdit* aAvcEdit, 
                               const TPtrC8& aVideoMimeType)
{
	iTranscoder = NULL;	
	iMonitor = aMonitor;
	iAvcEdit = aAvcEdit;

	iFrameSize = KVedResolutionQCIF;
	iFrameRate = 15.0;	
	iInputFrameRate = 15.0;
	iRandomAccessRate = KMinRandomAccessRate;

    // interpret and store video codec MIME-type. Allocates also iDataBuffer
    SetVideoCodecL( aVideoMimeType );	

	iStatus = NULL;	
	
	iVolReader = CVedVolReader::NewL();
	
	// allocate input picture
	iInputPicture = new (ELeave) TTRVideoPicture;
	
	// Create a timer 
	User::LeaveIfError(iTimer.CreateLocal());
	iTimerCreated = ETrue;
	
	// Add us to active scheduler
	CActiveScheduler::Add(this);
}


// ---------------------------------------------------------
// CVideoEncoder::~CVideoEncoder()
// Destructor
// ---------------------------------------------------------
//
CVideoEncoder::~CVideoEncoder()
    {

    Cancel();

    if ( iInputPicture )
	{
        delete iInputPicture;
		iInputPicture = 0;
	}
	
	if ( iDataBuffer )
	{	        
	    delete iDataBuffer;
	    iDataBuffer = 0;
	}
	
    if ( iTranscoder )
	{
        delete iTranscoder;
		iTranscoder = 0;
	}
	
	if ( iVolReader )
	{
	    delete iVolReader;
	    iVolReader = 0;
	}
    
    if ( iTimerCreated )
	{
        iTimer.Close();
		iTimerCreated = EFalse;
	}


    }


// ---------------------------------------------------------
// CVideoEncoder::SetVideoCodecL()
// Interpret and store video mime type
// ---------------------------------------------------------
//
void CVideoEncoder::SetVideoCodecL(const TPtrC8& aMimeType)
    {
    TBuf8<256> string;
    TBuf8<256> newMimeType;
    string = KVedMimeTypeH263;
    string += _L8( "*" );
	TInt dataBufferSize = KMaxCodedPictureSizeQCIF;

    iMaxFrameRate = 15.0;
    iArbitrarySizeAllowed = EFalse;
    
    if ( aMimeType.MatchF( (const TDesC8& )string ) != KErrNotFound ) 
        {
        // H.263

        newMimeType = KVedMimeTypeH263;

        if ( aMimeType.MatchF( _L8("*profile*") ) != KErrNotFound )
            {
            // profile given, check if we support it
            if ( aMimeType.MatchF( _L8("*profile=0*")) != KErrNotFound )
                {
                // profile 0 requested
                newMimeType += _L8( "; profile=0" );
                }
            else
                {
                // no other profiles supported
                PRINT((_L("CVideoEncoder::SetVideoCodecL() unsupported profile")));
                User::Leave(KErrNotSupported);
                }
            }
        else
            {
            // no profile is given => assume 0
            newMimeType += _L8( "; profile=0" );
            }

        if ( aMimeType.MatchF( _L8("*level=10*") ) != KErrNotFound )
            {
    		iMaxBitRate = iBitRate = KVedBitRateH263Level10;
    		iMaxResolution = KVedResolutionQCIF;
    		dataBufferSize = KMaxCodedPictureSizeQCIF;
            newMimeType += _L8( "; level=10" );
            }
        else if ( aMimeType.MatchF( _L8("*level=45*") ) != KErrNotFound )
            {
    		iMaxBitRate = iBitRate = KVedBitRateH263Level45;
    		iMaxResolution = KVedResolutionQCIF;
    		dataBufferSize = KMaxCodedPictureSizeQCIF;
            newMimeType += _L8( "; level=45" );
            }
        else if ( aMimeType.MatchF( _L8("*level*") ) != KErrNotFound )
            {
            // no other levels supported
            PRINT((_L("CVideoEncoder::SetVideoCodecL() unsupported level")));
            User::Leave(KErrNotSupported);
            }
        else
            {
            // if no level is given assume 10
    		iMaxBitRate = iBitRate = KVedBitRateH263Level10;
    		iMaxResolution = KVedResolutionQCIF;
    		dataBufferSize = KMaxCodedPictureSizeQCIF;
            newMimeType += _L8( "; level=10" );
            }
        }
    else
        {
        string = KVedMimeTypeMPEG4Visual;
        string += _L8( "*" );

        if ( aMimeType.MatchF( string ) != KErrNotFound ) 
            {
            // MPEG-4 Visual
            newMimeType = KVedMimeTypeMPEG4Visual;
            if ( aMimeType.MatchF( _L8("*profile-level-id=8*") ) != KErrNotFound )
                {
                // simple profile level 0
        		iMaxBitRate = iBitRate = KVedBitRateMPEG4Level0;
        		iMaxResolution = KVedResolutionQCIF;
                // define max size 10K
                dataBufferSize = KMaxCodedPictureSizeMPEG4QCIF;
                newMimeType += _L8("; profile-level-id=8");
                }
            else if ( aMimeType.MatchF( _L8("*profile-level-id=9*") ) != KErrNotFound )
                {
                // simple profile level 0b
        		iMaxBitRate = iBitRate = KVedBitRateMPEG4Level0;
        		iMaxResolution = KVedResolutionQCIF;
                // define max size 10K
                dataBufferSize = KMaxCodedPictureSizeMPEG4QCIF;
                newMimeType += _L8("; profile-level-id=9");
                }
            else if ( aMimeType.MatchF( _L8("*profile-level-id=1*") ) != KErrNotFound )
                {
                // simple profile level 1
        		iMaxBitRate = iBitRate = KVedBitRateMPEG4Level0;
        		iMaxResolution = KVedResolutionQCIF;
                // define max size 10K
                dataBufferSize = KMaxCodedPictureSizeMPEG4QCIF;
                iArbitrarySizeAllowed = ETrue;                
                newMimeType += _L8("; profile-level-id=1");
                }
            else if ( aMimeType.MatchF( _L8("*profile-level-id=2*") ) != KErrNotFound )
                {
                // simple profile level 2
			    dataBufferSize = KMaxCodedPictureSizeMPEG4CIF;
        		iMaxResolution = KVedResolutionCIF;
			    iMaxBitRate = iBitRate = KVedBitRateMPEG4Level2;
                iArbitrarySizeAllowed = ETrue;                
                newMimeType += _L8("; profile-level-id=2");
                }
            else if ( aMimeType.MatchF( _L8("*profile-level-id=3*") ) != KErrNotFound )
                {
                // simple profile level 3
			    dataBufferSize = KMaxCodedPictureSizeMPEG4CIF;
			    iMaxBitRate = iBitRate = KVedBitRateMPEG4Level2;
        		iMaxResolution = KVedResolutionCIF;
			    iMaxFrameRate = 30.0;
                iArbitrarySizeAllowed = ETrue;                
                newMimeType += _L8("; profile-level-id=3");
                }
            else if ( aMimeType.MatchF( _L8("*profile-level-id=4*") ) != KErrNotFound )
                {
                // simple profile level 4a
			    iMaxBitRate = iBitRate = KVedBitRateMPEG4Level4A;	        	
			    dataBufferSize = KMaxCodedPictureSizeVGA;
        		iMaxResolution = KVedResolutionVGA;
			    iMaxFrameRate = 30.0;
                iArbitrarySizeAllowed = ETrue;                
                newMimeType += _L8("; profile-level-id=4");
                }
            else if ( aMimeType.MatchF( _L8("*profile-level-id=*") ) != KErrNotFound )
                {
                // no other profile-level ids supported
                PRINT((_L("CVideoEncoder::SetVideoCodecL() unsupported MPEG-4 profile-level")));
                User::Leave(KErrNotSupported);
                }
            else
                {
                // Default is level 0 in our case (normally probably level 1)
        		iMaxBitRate = iBitRate = KVedBitRateMPEG4Level0;
        		iMaxResolution = KVedResolutionQCIF;
                // define max size 10K
                dataBufferSize = KMaxCodedPictureSizeMPEG4QCIF;
                newMimeType += _L8("; profile-level-id=8");
                }
            }
        else
            {
             
#ifdef VIDEOEDITORENGINE_AVC_EDITING
            string = KVedMimeTypeAVC;
            string += _L8( "*" );                
            if ( aMimeType.MatchF( string ) != KErrNotFound ) 
                {
                // AVC
                newMimeType = KVedMimeTypeAVC;
                if ( aMimeType.MatchF( _L8("*profile-level-id=42800A*") ) != KErrNotFound )
                    {
                    // baseline profile level 1
            		iMaxBitRate = iBitRate = KVedBitRateAVCLevel1;
            		iMaxResolution = KVedResolutionQCIF;                        
                    dataBufferSize = KMaxCodedPictureSizeAVCLevel1;
                    iArbitrarySizeAllowed = ETrue;                
                    newMimeType += _L8("; profile-level-id=42800A");
                    }                    
                else if ( aMimeType.MatchF( _L8("*profile-level-id=42900B*") ) != KErrNotFound )
                    {                                                
                    // baseline profile level 1b
                    iMaxBitRate = iBitRate = KVedBitRateAVCLevel1b;
            		iMaxResolution = KVedResolutionQCIF;                        
                    dataBufferSize = KMaxCodedPictureSizeAVCLevel1B;
                    iArbitrarySizeAllowed = ETrue;                
                    newMimeType += _L8("; profile-level-id=42900B");
                    }
                else if ( aMimeType.MatchF( _L8("*profile-level-id=42800B*") ) != KErrNotFound )
                    {
                    // baseline profile level 1.1
                    iMaxBitRate = iBitRate = KVedBitRateAVCLevel1_1;
            		iMaxResolution = KVedResolutionCIF;                        
                    dataBufferSize = KMaxCodedPictureSizeAVCLevel1_1;
                    iArbitrarySizeAllowed = ETrue;                
                    newMimeType += _L8("; profile-level-id=42800B");
                    }
                else if ( aMimeType.MatchF( _L8("*profile-level-id=42800C*") ) != KErrNotFound )
                    {
                    // baseline profile level 1.2
                    iMaxBitRate = iBitRate = KVedBitRateAVCLevel1_2;
            		iMaxResolution = KVedResolutionCIF;                        
                    dataBufferSize = KMaxCodedPictureSizeAVCLevel1_2;
                    iArbitrarySizeAllowed = ETrue;                
                    newMimeType += _L8("; profile-level-id=42800C");                    
                    }      
                //WVGA task
                else if ( aMimeType.MatchF( _L8("*profile-level-id=42801F*") ) != KErrNotFound )
                    {
                    // baseline profile level 1.3
                    iMaxBitRate = iBitRate = KVedBitRateAVCLevel3_1;
            		iMaxResolution = KVedResolutionWVGA;                        
                    dataBufferSize = KMaxCodedPictureSizeAVCLevel3_1;
                    iArbitrarySizeAllowed = ETrue;                
                    newMimeType += _L8("; profile-level-id=42801F");                    
                    }
                else if ( aMimeType.MatchF( _L8("*profile-level-id=*") ) != KErrNotFound )
                    {
                    // no other profile-level ids supported
                    PRINT((_L("CVideoEncoder::SetVideoCodecL() unsupported AVC profile-level")));
                    User::Leave(KErrNotSupported);
                    }
                else
                    {
                        // Default is level 1 (?)
                        iMaxBitRate = iBitRate = KVedBitRateAVCLevel1;
                		iMaxResolution = KVedResolutionQCIF;                            
                        dataBufferSize = KMaxCodedPictureSizeAVCLevel1;
                        newMimeType += _L8("; profile-level-id=42800A");
                    }                
                }
            else
                {
                    // unknown mimetype
                    User::Leave( KErrNotSupported );
                }                                        
#else
                    // unknown mimetype
                    User::Leave( KErrNotSupported );
#endif
            }
        }

    // successfully interpreted the input mime type
    iMimeType = newMimeType;
    if ( iDataBuffer )
        {
        delete iDataBuffer;
        iDataBuffer = NULL;
        }
	iDataBuffer = (HBufC8*) HBufC8::NewL(dataBufferSize);    

    }
    
// ---------------------------------------------------------
// CVideoEncoder::SetFrameSizeL
// Sets the used frame size
// (other items were commented in a header).
// ---------------------------------------------------------
//
void CVideoEncoder::SetFrameSizeL(const TSize& aSize)
    {
  
    if (iFrameSize == aSize)
        return;

    if ( (aSize.iWidth > iMaxResolution.iWidth) || (aSize.iHeight > iMaxResolution.iHeight))
        {
        if ( iArbitrarySizeAllowed ) // This is for future-proofness. Currently the checking of standard sizes below overrules this one
            {
            if ( aSize.iWidth * aSize.iHeight > iMaxResolution.iWidth*iMaxResolution.iHeight )
                {
                PRINT((_L("CVideoEncoder::SetFrameSizeL() too high resolution requested")));
                User::Leave( KErrNotSupported );
                }
            }
        else
            {
            PRINT((_L("CVideoEncoder::SetFrameSizeL() incompatible or too high resolution requested")));
            User::Leave( KErrNotSupported );
            }
        }
    
	// check new size. For now only standard sizes are allowed
	if ( (aSize != KVedResolutionSubQCIF) && 
		 (aSize != KVedResolutionQCIF) &&
		 (aSize != KVedResolutionCIF) &&
		 (aSize != KVedResolutionQVGA) &&
		 (aSize != KVedResolutionVGA16By9) &&
		 (aSize != KVedResolutionVGA) &&
		 //WVGA task
		 (aSize != KVedResolutionWVGA) )
	{
		 User::Leave( KErrArgument );
	}

    iFrameSize = aSize;    
    
    }

// ---------------------------------------------------------
// CVideoEncoder::GetFrameSize
// Gets the used frame size
// (other items were commented in a header).
// ---------------------------------------------------------
//
void CVideoEncoder::FrameSize(TSize& aSize) const
    {

    aSize = iFrameSize;

    }

// ---------------------------------------------------------
// CVideoEncoder::SetFrameRate
// Sets the used targt frame rate
// (other items were commented in a header).
// ---------------------------------------------------------
//
TInt CVideoEncoder::SetFrameRate(const TReal aFrameRate)
{
    VEASSERT(aFrameRate > 0.0);

    if ( aFrameRate <= iMaxFrameRate )
        {
        iFrameRate = TReal32(aFrameRate);
        }
    else
        {
        iFrameRate = iMaxFrameRate;
        }
        
    // target framerate cannot be larger than source framerate
    if (iFrameRate > iInputFrameRate)
        iFrameRate = iInputFrameRate;

    return KErrNone;
}

// ---------------------------------------------------------
// CVideoEncoder::SetInputFrameRate
// Sets the used input sequence frame rate
// (other items were commented in a header).
// ---------------------------------------------------------
//
TInt CVideoEncoder::SetInputFrameRate(const TReal aFrameRate)
    {
    
    VEASSERT(aFrameRate > 0.0);
         
    iInputFrameRate = aFrameRate;
        
    // target framerate cannot be larger than source framerate
    if (iFrameRate > iInputFrameRate)
        iFrameRate = iInputFrameRate;
    
    return KErrNone;
        
    }

    
// ---------------------------------------------------------
// CVideoEncoder::SetRandomAccessRate
// Sets the used target random access rate
// (other items were commented in a header).
// ---------------------------------------------------------
//
TInt CVideoEncoder::SetRandomAccessRate(const TReal aRate)
{
    VEASSERT(aRate > 0.0);

    iRandomAccessRate = aRate;

    return KErrNone;
}

// ---------------------------------------------------------
// CVideoEncoder::SetBitrate
// Sets the used target bitrate
// (other items were commented in a header).
// ---------------------------------------------------------
//
TInt CVideoEncoder::SetBitrate(const TInt aBitrate)
{    
    VEASSERT(aBitrate > 0);

    if ( aBitrate <= iMaxBitRate )
        {
        iBitRate = aBitrate;
        }
    else
        {
        iBitRate = iMaxBitRate;
        }

    return KErrNone;

}

// --------------------------------------------------------



// ---------------------------------------------------------
// CVideoEncoder::InitializeL
// Initializes the encoder
// (other items were commented in a header).
// ---------------------------------------------------------
//
void CVideoEncoder::InitializeL(TRequestStatus &aStatus)
    {
    
    PRINT((_L("CVideoEncoder::InitializeL() in")));

    iResetRequestStatus = &aStatus;

    // Create DevVideoRecord & select video encoder

    if ( iTranscoder ) 
        {
        // need to recreate since parameters changed
        delete iTranscoder;
        iTranscoder = 0;
        }

    iFatalError = EFalse;

    iTranscoder = CTRTranscoder::NewL(*this);

    // Select & set parameters to transcoder    
    TRAPD(err, SetupEncoderL());
        
    if ( err != KErrNone )
        {
        // error
        User::Leave( err );
        }
    
    iTranscoder->InitializeL();
    
    PRINT((_L("CVideoEncoder::InitializeL() out")));
    

    }

// ---------------------------------------------------------
// CVideoEncoder::Start
// Starts the encoder
// (other items were commented in a header).
// ---------------------------------------------------------
//

void CVideoEncoder::Start()
{
    TRAPD( error, iTranscoder->StartL() );
    
    if (error != KErrNone)
        {
        if (iMonitor)
            iMonitor->Error(error);
        }
}


// ---------------------------------------------------------
// CVideoEncoder::SetRandomAccessPoint
// Forces the next encoded frame to Intra
// (other items were commented in a header).
// ---------------------------------------------------------
//
void CVideoEncoder::SetRandomAccessPoint()
{
    VEASSERT(iTranscoder);
    
    iTranscoder->SetRandomAccessPoint();
       
}

// ---------------------------------------------------------
// CVideoEncoder::Stop
// Stops the encoder
// (other items were commented in a header).
// ---------------------------------------------------------
//
void CVideoEncoder::Stop()
{
	
    if ( !iFatalError )
        {
        
        if (iStarted)
            {
            TRAPD( error, iTranscoder->StopL() );
            if (error != KErrNone)
                {
                if (iMonitor)
                    iMonitor->Error(error);
                }
            }
        
        iStarted = EFalse;
        }
}
	

// ---------------------------------------------------------
// CVideoEncoder::Reset
// Resets the encoder
// (other items were commented in a header).
// ---------------------------------------------------------
//
void CVideoEncoder::Reset(TRequestStatus &aStatus)
{

    PRINT((_L("CVideoEncoder::Reset() in")));    

    iPreviousTimeStamp = TTimeIntervalMicroSeconds(0);

    iResetRequestStatus = &aStatus;

    if ( iFatalError )
        {
        MtroAsyncStopComplete();
        }
    else
        {
        TRAPD(err, iTranscoder->AsyncStopL());
        if (err != KErrNone)
            iMonitor->Error(err);
        }

	iStarted = EFalse;

    PRINT((_L("CVideoEncoder::Reset() out")));
}

void CVideoEncoder::Reset()
{

    PRINT((_L("CVideoEncoder::Reset() (sync.) in")));

    iPreviousTimeStamp = TTimeIntervalMicroSeconds(0);

    if ( !iFatalError )
    {
        if (iStarted) 
        {            
            TRAPD(err, iTranscoder->StopL());
            if (err != KErrNone)
                iMonitor->Error(err);
        }
    }

	iStarted = EFalse;

    PRINT((_L("CVideoEncoder::Reset() (sync.) out")));
}

// ---------------------------------------------------------
// CVideoEncoder::RunL
// Active object running method.
// (other items were commented in a header).
// ---------------------------------------------------------
//
void CVideoEncoder::RunL()
{

    // Timer elapsed, complete encoding request
    // with KErrCancel

    PRINT((_L("CVideoEncoder::RunL() in")));

    if (iTimerRequestPending)
    {
        iTimerRequestPending = EFalse;
        if (iEncodeRequestStatus)
        {
#ifdef _DEBUG
            TTime current;
            current.UniversalTime();
            TInt64 time = current.MicroSecondsFrom(iEncodeStartTime).Int64();    
            PRINT((_L("CVideoEncoder::RunL(), completing request, time since encoding started %d"), I64INT( time )));
#endif

            VEASSERT(*iEncodeRequestStatus == KRequestPending);
            // complete request
            User::RequestComplete(iEncodeRequestStatus, KErrCancel);
            iEncodeRequestStatus = 0;
			iEncodePending = EFalse;
        }
    }
    PRINT((_L("CVideoEncoder::RunL() out")));

}

// ---------------------------------------------------------
// CVideoEncoder::RunError
// Active object error method.
// (other items were commented in a header).
// ---------------------------------------------------------
//
TInt CVideoEncoder::RunError(TInt aError)
{
    PRINT((_L("CVideoEncoder::RunError() in")));

    Cancel();
        
    iMonitor->Error(aError);

    PRINT((_L("CVideoEncoder::RunError() out")));
    
    return KErrNone;
}

// ---------------------------------------------------------
// CVideoEncoder::DoCancel
// Active object cancelling method
// (other items were commented in a header).
// ---------------------------------------------------------
//
void CVideoEncoder::DoCancel()
{

    PRINT((_L("CVideoEncoder::DoCancel() in")));

    // Cancel our timer request if we have one
    if ( iTimerRequestPending )
    {
        iTimer.Cancel();
        iTimerRequestPending = EFalse;
        return;
    }

}
       
// ---------------------------------------------------------
// CVideoEncoder::EncodeFrameL
// Encodes a frame
// (other items were commented in a header).
// ---------------------------------------------------------
//
void CVideoEncoder::EncodeFrameL(TPtr8& aYUVFrame, TRequestStatus &aStatus, TTimeIntervalMicroSeconds aTimeStamp)
    {
    PRINT((_L("CVideoEncoder::EncodeFrameL() in, aTimeStamp = %d"), I64INT( aTimeStamp.Int64() ) ));

    if ( iFatalError )
        {
        PRINT((_L("CVideoEncoder::EncodeFrameL() can't encode since fatal error has occurred earlier")));
        User::Leave( KErrGeneral );
        }

    iEncodeRequestStatus = &aStatus;

	if (!iStarted)
	{
		PRINT((_L("CVideoEncoder::EncodeFrameL() - starting devVideoRec")));
		//iTranscoder->StopL();  // NOTE: needed ??
		iTranscoder->StartL();
		iStarted = ETrue;
	}	 

    // wrap input frame to encoder input buffer
    iInputBuffer.Set(aYUVFrame);
    iInputPicture->iRawData = &iInputBuffer;   
    iInputPicture->iDataSize.SetSize(iFrameSize.iWidth, iFrameSize.iHeight);

    if (aTimeStamp > TTimeIntervalMicroSeconds(0))
    {
        TInt64 diff = aTimeStamp.Int64() - iPreviousTimeStamp.Int64();        

        if (diff < 0)
        {
            aTimeStamp = iPreviousTimeStamp.Int64() + TInt64(66667);
        }
        // NOTE: Could the real difference between two consecutive
        //       frames be used instead of assuming 15 fps ?
    }
    iPreviousTimeStamp = aTimeStamp;

    iInputPicture->iTimestamp = aTimeStamp;
    //iInputPicture->iLink = NULL;
    iInputPicture->iUser = this;

#ifdef _DEBUG
    iEncodeStartTime.UniversalTime();
#endif


	iEncodePending = ETrue;

    iTranscoder->SendPictureToTranscoderL( iInputPicture );

    PRINT((_L("CVideoEncoder::EncodeFrameL() out")));

    }


// ---------------------------------------------------------
// CVideoEncoder::GetBuffer
// Gets encoded frame bitstream buffer
// (other items were commented in a header).
// ---------------------------------------------------------
//
TPtrC8& CVideoEncoder::GetBufferL(TBool& aKeyFrame)
    {
    if ( iFatalError )
        {
        User::Leave( KErrNotReady );
        }

    VEASSERT(iDataBuffer->Length() > 0);

    PRINT((_L("CVideoEncoder::GetBufferL(), keyFrame = %d"), iKeyFrame));    

    aKeyFrame = iKeyFrame;    
        
    iReturnDes.Set(iDataBuffer->Des()); 
    return iReturnDes;    
 
    }

// ---------------------------------------------------------
// CVideoEncoder::ReturnBuffer
// Returns used bitstream buffer to devVideoRec
// (other items were commented in a header).
// ---------------------------------------------------------
//
void CVideoEncoder::ReturnBuffer()
    {
    if ( iFatalError )
        {
        return;
        }
        
    iDataBuffer->Des().Zero();

    }

// ---------------------------------------------------------
// CVideoEncoder::SetupEncoderL
// Private helper method to select & setup the encoder 
// plugin devvr must use
// (other items were commented in a header).
// ---------------------------------------------------------
//
void CVideoEncoder::SetupEncoderL()
    {

    if ( !(iTranscoder->SupportsOutputVideoFormat(iMimeType) ) )
        {
        User::Leave(KErrNotSupported);
        }             
        
    TTRVideoFormat videoInputFormat;
    TTRVideoFormat videoOutputFormat;
    
    videoInputFormat.iSize = iFrameSize;
    videoInputFormat.iDataType = CTRTranscoder::ETRYuvRawData420;

    videoOutputFormat.iSize = iFrameSize;
    videoOutputFormat.iDataType = CTRTranscoder::ETRDuCodedPicture;
        
    iTranscoder->OpenL( this,
                        CTRTranscoder::EEncoding,
                        KNullDesC8,
                        iMimeType,
                        videoInputFormat,
                        videoOutputFormat,
                        EFalse );

    // default, will be updated by ParseVolHeader
    iTimeIncrementResolution = KTimeIncrementResolutionHW;    //KTimeIncrementResolutionSW;
    
    iTranscoder->SetVideoBitRateL(iBitRate);
    
    iTranscoder->SetFrameRateL(iFrameRate);    
           
    iTranscoder->SetChannelBitErrorRateL(0.0);
       
    // Get processing time estimate from transcoder, divide it by the framerate to get processing time per frame
    // and then multiply it by 2 to get some safety margin and by unit conversion factor 1000000. 
    // The delay is used to determine if a frame was skipped, hence there should be some margin.
#ifdef __WINSCW__
    iMaxEncodingDelay = 5000000;    // emulator can be really slow, use 5 seconds timeout
#else    
    iMaxEncodingDelay = (TUint)(2*1000000*iTranscoder->EstimateTranscodeTimeFactorL(videoInputFormat,videoOutputFormat)/iFrameRate);
    iMaxEncodingDelay += 100000;  // Add 100 ms for safety margin
#endif
    
    TTRVideoCodingOptions codingOptions;
    codingOptions.iSyncIntervalInPicture = 0;
    codingOptions.iMinRandomAccessPeriodInSeconds = (TInt) (1.0 / iRandomAccessRate);
    
    // NOTE: What about these ???
    codingOptions.iDataPartitioning = EFalse;
    codingOptions.iReversibleVLC = EFalse;
    codingOptions.iHeaderExtension = 0;
   
    iTranscoder->SetVideoCodingOptionsL(codingOptions);
   
    iTranscoder->SetVideoPictureSinkOptionsL(iFrameSize, this);

    }


// -----------------------------------------------------------------------------
// CVideoEncoder::MtroSetInputFrameRate
// Sets input sequence frame rate for encoding
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CVideoEncoder::MtroSetInputFrameRate(TReal& aRate)
    {
     
    aRate = iInputFrameRate;
        
    }


// -----------------------------------------------------------------------------
// CVideoEncoder::MtroPictureFromTranscoder
// Called by transcoder to return the input picture buffer
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CVideoEncoder::MtroPictureFromTranscoder(TTRVideoPicture* aPicture)
    {
    PRINT((_L("CVideoEncoder::MtroPictureFromTranscoder() %x"), aPicture));
    
    if (iInputPicture != aPicture)
    {
        iMonitor->Error(KErrGeneral);
        return;
    }
        
    if (iEncodePending)
        {                
        if (!iTimerRequestPending)
            {
            // Activate timer to wait for encoding, if the encoding didn't complete already. 
            // Timeout is needed since we don't get notification if frame was skipped.
            SetActive();
            iStatus = KRequestPending;        
            iTimer.After(iStatus, TTimeIntervalMicroSeconds32(iMaxEncodingDelay));
            iTimerRequestPending = ETrue;
            }
        }
    }


// -----------------------------------------------------------------------------
// CVideoEncoder::WriteBufferL
// Called by transcoder to notify that new bitsream buffer is ready
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CVideoEncoder::WriteBufferL(CCMRMediaBuffer* aBuffer)
    {
    
    PRINT((_L("CVideoEncoder::WriteBufferL(), iEncodePending = %d"), iEncodePending));
    
#ifdef _DEBUG
    TTime current;
    current.UniversalTime();
    TInt64 time = current.MicroSecondsFrom(iEncodeStartTime).Int64();
	PRINT((_L("CVideoEncoder::WriteBufferL(), time spent encoding = %d ms, iEncodeRequestStatus = 0x%x"), 
		(I64INT(time))/1000 ,iEncodeRequestStatus));

#endif

    if (aBuffer->Type() == CCMRMediaBuffer::EVideoMPEG4DecSpecInfo)
	{
		// copy data to bitstream buffer
	    TPtr8 ptr = iDataBuffer->Des();		    
	    ptr.Copy( aBuffer->Data() );	    
	    return;		
	}

    // Cancel timer
    Cancel();

	iEncodePending = EFalse;	
	
	if (aBuffer->BufferSize() == 0)
	{
	    PRINT((_L("CVideoEncoder::WriteBufferL() buffer length == 0")));
	    
    	if ( iEncodeRequestStatus != 0 )
        {
    		// complete request
    		User::RequestComplete(iEncodeRequestStatus, KErrUnderflow);
    		iEncodeRequestStatus = 0;
    	}	    
	    return;
	}
		
	VEASSERT(aBuffer->Data().Length() <= KMaxCodedPictureSizeVGA);		    
	
#ifdef VIDEOEDITORENGINE_AVC_EDITING
    TInt avcFrameLen = 0;
    if ( iMimeType.FindF(KVedMimeTypeAVC) != KErrNotFound )
    {           
       // get avc frame length
       TUint8* tmp = const_cast<TUint8*>(aBuffer->Data().Ptr() + aBuffer->Data().Length());
       // support for 1 frame in 1 NAL unit currently
       tmp -= 4;
       TInt numNalUnits = TInt(tmp[0]) + (TInt(tmp[1])<<8) + (TInt(tmp[2])<<16) + (TInt(tmp[3])<<24);
       if (numNalUnits > 0) { }
       
       VEASSERT( numNalUnits == 1 );       
       tmp -=4;
              
       avcFrameLen = tmp[0] + (TInt(tmp[1])<<8) + (TInt(tmp[2])<<16) + (TInt(tmp[3])<<24);                     
       
       TInt error = KErrNone;
       // check that there is enough room for length field and the frame
       if ( iDataBuffer->Des().MaxLength() < (avcFrameLen + 4) )
           TRAP( error, iDataBuffer->ReAllocL(avcFrameLen + 4) );
           
       if (error != KErrNone)
       {
           iMonitor->Error(error);
           return;
       }
       
       TUint8* buf = const_cast<TUint8*>(iDataBuffer->Des().Ptr());
       
       // set length
       buf[0] = TUint8((avcFrameLen >> 24) & 0xff);
       buf[1] = TUint8((avcFrameLen >> 16) & 0xff);
       buf[2] = TUint8((avcFrameLen >> 8) & 0xff);
       buf[3] = TUint8(avcFrameLen & 0xff);
       
       iDataBuffer->Des().SetLength(4);
    }
#endif        
			
	// copy data to bitstream buffer
	TPtr8 ptr = iDataBuffer->Des();
	
#ifdef VIDEOEDITORENGINE_AVC_EDITING
	if ( iMimeType.FindF(KVedMimeTypeAVC) != KErrNotFound )
	{
	    ptr.Append( aBuffer->Data().Ptr(), avcFrameLen );
	}
	else
#endif
    {
        ptr.Append( aBuffer->Data() );
    }
	
	// save keyframe flag
	iKeyFrame = aBuffer->RandomAccessPoint();
			
	if ( iEncodeRequestStatus != 0 )
    {
		// complete request
		User::RequestComplete(iEncodeRequestStatus, KErrNone);
		iEncodeRequestStatus = 0;
	}

    // --> user calls GetBuffer();	    
    
    }
    
TInt CVideoEncoder::SetVideoFrameSize(TSize /*aSize*/)
    {
    return KErrNone;    
    }
    
TInt CVideoEncoder::SetAverageVideoBitRate(TInt /*aSize*/)
    {
    return KErrNone;    
    }
    
TInt CVideoEncoder::SetMaxVideoBitRate(TInt /*aSize*/)
    {
    return KErrNone;    
    }
    
TInt CVideoEncoder::SetAverageAudioBitRate(TInt /*aSize*/)
    {
    return KErrNone;    
    }

// -----------------------------------------------------------------------------
// CVideoEncoder::MtroFatalError
// Called by transcoder when a fatal error has occurred
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CVideoEncoder::MtroFatalError(TInt aError)
    {

    PRINT((_L("CVideoEncoder::MtroFatalError() %d"), aError));
    iFatalError = ETrue;

    iMonitor->Error(aError);
    
    }
    
void CVideoEncoder::MtroReturnCodedBuffer(CCMRMediaBuffer* /*aBuffer*/)
    {
        
        User::Panic(_L("CVIDEOENCODER"), EInternalAssertionFailure);
    }


// -----------------------------------------------------------------------------
// CVideoEncoder::MdvroInitializeComplete
// Called by transcoder when initialization is complete
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CVideoEncoder::MtroInitializeComplete(TInt aError)
    {
    PRINT((_L("CVideoEncoder::MtroInitializeComplete %d, iResetRequestStatus %x"), aError, iResetRequestStatus));

    VEASSERT(iResetRequestStatus);

    if ( aError != KErrNone )    
    {        
        iMonitor->Error(aError);
        if ( iResetRequestStatus != 0 )
        {            
            User::RequestComplete(iResetRequestStatus, aError);
            iResetRequestStatus = 0;
        }
        return;
    }
    
    TInt error = KErrNone;    
    // Handle MPEG-4 VOL / AVC SPS/PPS data reading/writing            
    TRAP(error, HandleCodingStandardSpecificInfoL());
    
    if ( error != KErrNone )
        iMonitor->Error(error); 

	if ( iResetRequestStatus != 0 )
    {		
		PRINT((_L("CVideoEncoder::MtroInitializeComplete() complete request")));
		// complete request:
        User::RequestComplete(iResetRequestStatus, error);
		iResetRequestStatus = 0;
	}
    
    }

// -----------------------------------------------------------------------------
// CVideoEncoder::MtroAsyncStopComplete
// Called by transcoder when all pictures have been processed
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CVideoEncoder::MtroAsyncStopComplete()
    {
    
    PRINT((_L("CVideoEncoder::MtroAsyncStopComplete()")));
    
    if ( iResetRequestStatus != 0 )
    {
		PRINT((_L("CVideoEncoder::MdvroStreamEnd() complete request")));
        // complete request
        User::RequestComplete(iResetRequestStatus, KErrNone);
        iResetRequestStatus = 0;
    }
       
    }

// -----------------------------------------------------------------------------
// CVideoEncoder::GetTimeIncrementResolution
// Gets time increment resolution used in MPEG-4 
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TInt CVideoEncoder::GetTimeIncrementResolution() const
    {
    if ( iVolReader->TimeIncrementResolution() != 0 )
        return iVolReader->TimeIncrementResolution();
    
    return iTimeIncrementResolution;
    }
    
// -----------------------------------------------------------------------------
// CVideoEncoder::HandleCodingStandardSpecificInfoL
// Parses the MPEG-4 VOL header / AVC Dec. configuration record
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CVideoEncoder::HandleCodingStandardSpecificInfoL()
    {

    // Parse the VOL header for mpeg-4
    if ( iMimeType.FindF(KVedMimeTypeMPEG4Visual) != KErrNotFound )
        {
        HBufC8* headerData = iTranscoder->GetCodingStandardSpecificInitOutputLC();
        if ( headerData != 0 )
            {
            iVolReader->ParseVolHeaderL( (TDesC8&) *headerData );
            
            // Insert VOL header to bitstream buffer
	        TPtr8 ptr = iDataBuffer->Des();		
	        ptr.Append( *headerData );
            
            CleanupStack::PopAndDestroy( headerData );
            }
        }

#ifdef VIDEOEDITORENGINE_AVC_EDITING         
    // Parse & write SPS/PPS data for AVC
    else if ( iMimeType.FindF(KVedMimeTypeAVC) != KErrNotFound )
        {
        HBufC8* headerData = iTranscoder->GetCodingStandardSpecificInitOutputLC();
        
        if ( headerData != 0 )
            {   
            
            HBufC8* outputAVCHeader = 0;
            // is the max. size of AVCDecoderConfigurationRecord known here ??
            outputAVCHeader = (HBufC8*) HBufC8::NewLC(16384);            
            TPtr8 ptr = outputAVCHeader->Des();
	            
	        // parse header & convert it to AVCDecoderConfigurationRecord -format
	        iAvcEdit->ConvertAVCHeaderL(*headerData, ptr);	            
	        
	        // save it to avc module for later use
	        iAvcEdit->SaveAVCDecoderConfigurationRecordL(ptr, ETrue /* aFromEncoder */); 

            CleanupStack::PopAndDestroy( 2 );  // headerData, outputAVCHeader
            
            }
        }
#endif
        
    }