diff -r 000000000000 -r 951a5db380a0 videoeditorengine/vedengine/videoprocessor/src/VideoEncoderMDF.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/videoeditorengine/vedengine/videoprocessor/src/VideoEncoderMDF.cpp Fri Jan 29 14:08:33 2010 +0200 @@ -0,0 +1,1285 @@ +/* +* 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 +#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(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(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 + + }