diff -r 000000000000 -r 951a5db380a0 videoeditorengine/vedengine/videoprocessor/src/videoprocessor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/videoeditorengine/vedengine/videoprocessor/src/videoprocessor.cpp Fri Jan 29 14:08:33 2010 +0200 @@ -0,0 +1,6628 @@ +/* +* 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 processor. +* +*/ + + +// Include Files + +#include "vedcommon.h" +#include "movieprocessorimpl.h" +#include "statusmonitor.h" +#include "activequeue.h" +#include "dataprocessor.h" +#include "h263dmai.h" // CVedH263Dec +#include "mp4parser.h" +#include "videoencoder.h" +#include "videoprocessor.h" +#include "mpeg4timer.h" +#include "vedvolreader.h" +#include "vedvideosettings.h" +#include "vedavcedit.h" + +// Local constants +const TUint KInitialDataBufferSize = 8192; // initial frame data buffer size +const TUint KH263StartCodeLength = 3; // H.263 picture start code length +const TUint KMPEG4StartCodeLength = 4; // MPEG4 picture start code length +//const TUint KMaxEncodingDelay = 500000; // time to wait for encoding to complete in microsec. +const TUint KDefaultTimeIncrementResolution = 30000; +const TUint KAVCNotCodedFrameBuffer = 128; +const TUint KMaxItemsInProcessingQueue = 3; // note! this must be synchronized with KTRMinNumberOfBuffersCodedPicture setting in transcoder! + + +#ifdef _DEBUG +const TInt KErrorCode = CVideoProcessor::EDecoderFailure; +#else +const TInt KErrorCode = KErrGeneral; +#endif + +// An assertion macro wrapper to clean up the code a bit +#define VDASSERT(x, n) __ASSERT_DEBUG(x, User::Panic(_L("CVideoProcessor"), EInternalAssertionFailure+n)) + +// Debug print macro + +#ifdef _DEBUG +#include +#define PRINT(x) RDebug::Print x; +#else +#define PRINT(x) +#endif + +// ================= STATIC FUNCTIONS ======================= + +// --------------------------------------------------------- +// AddBits +// Static helper function to add bits to byte-buffer +// --------------------------------------------------------- +// +static void AddBits(TUint8* aBuf, TInt& aBitIndex, TInt& aByteIndex, TInt aBits, TInt aNrOfBits) + { + // aBitIndex = 8 => first bit in the left + // aBitIndex = 1 => last bit in the right + while ( aBitIndex < aNrOfBits ) + { + // divide into n bytes + aBuf[aByteIndex++] |= TUint8( aBits >> (aNrOfBits-aBitIndex) ); + aNrOfBits -= aBitIndex; + aBitIndex = 8; + } + // all bits fit into 1 byte + aBitIndex -= aNrOfBits; + aBuf[aByteIndex] |= TUint8( aBits << aBitIndex ); + if (aBitIndex == 0) + { + aBitIndex = 8; + aByteIndex++; + } + } + + + +// ================= MEMBER FUNCTIONS ======================= + + + +// --------------------------------------------------------- +// CVideoProcessor::NewL +// Symbian two-phased constructor. +// --------------------------------------------------------- +// + +CVideoProcessor* CVideoProcessor::NewL(CActiveQueue *anInputQueue, + CVideoProcessor::TStreamParameters *aStreamParameters, + CMovieProcessorImpl* aProcessor, + CStatusMonitor *aStatusMonitor, + CVedAVCEdit *aAvcEdit, + TBool aThumbnailMode, + TInt aPriority) +{ + CVideoProcessor *self = new (ELeave) CVideoProcessor(anInputQueue, + aStreamParameters, + aProcessor, + aStatusMonitor, + aAvcEdit, + aThumbnailMode, + aPriority); + + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + + return self; + +} + +// --------------------------------------------------------- +// CVideoProcessor::CVideoProcessor +// Constructor. +// --------------------------------------------------------- +// +CVideoProcessor::CVideoProcessor(CActiveQueue *anInputQueue, + TStreamParameters *aStreamParameters, + CMovieProcessorImpl* aProcessor, + CStatusMonitor *aStatusMonitor, + CVedAVCEdit *aAvcEdit, + TBool aThumbnailMode, + TInt aPriority) : CVideoDecoder(aPriority), + iWriteDes(0,0), + iThumbnailMode(aThumbnailMode) +{ + + // Remember the objects + iQueue = anInputQueue; + iMonitor = aStatusMonitor; + iProcessor = aProcessor; + iAvcEdit = aAvcEdit; + + // Remember the stream parameters + iVideoWidth = aStreamParameters->iWidth; + iVideoHeight = aStreamParameters->iHeight; + + // Color Toning + iFirstFrameQp = 0; + + iTiming = aStreamParameters->iTiming; + // Reset state + iReaderSet = EFalse; + iDecoding = EFalse; + iStreamEnd = EFalse; + iPreviousFrameIncluded = EFalse; + iFrameOperation = EDecodeAndWrite; + + iTrPrevious = -1; + + iFirstFrameFlag = ETrue; + iDecodePending = EFalse; + + iTranscoderStarted = EFalse; + iDecodingSuspended = EFalse; + + iStartTransitionColor = EColorNone; + iEndTransitionColor = EColorNone; + iStartNumberOfTransitionFrames = KNumTransitionFrames; + iEndNumberOfTransitionFrames = KNumTransitionFrames; + iNextTransitionNumber = -1; + iProcessingComplete = EFalse; + iStreamEndRead = EFalse; + + iPreviousTimeStamp = TTimeIntervalMicroSeconds(-1); + + iFirstRead = ETrue; + + iThumbDecoded = EFalse; + + iMaxItemsInProcessingQueue = KMaxItemsInProcessingQueue; + + iDataFormat = EDataUnknown; + + iLastWrittenFrameNumber = -1; + + iInitializing = ETrue; +} + + +// --------------------------------------------------------- +// CVideoProcessor::~CVideoProcessor() +// Destructor +// --------------------------------------------------------- +// +CVideoProcessor::~CVideoProcessor() +{ + + // If we are decoding, stop + if (iDecoding) + Stop(); + + // Remove from being a reader + if (iReaderSet) + { + // Return current block and all + // blocks from input queue + if (iBlock) + { + if (iQueue) + iQueue->ReturnBlock(iBlock); + } + + if (iQueue) + iBlock = iQueue->ReadBlock(); + + while (iBlock) + { + if (iQueue) + { + iQueue->ReturnBlock(iBlock); + iBlock = iQueue->ReadBlock(); + } + } + iBlockPos = 0; + + if (iQueue) + iQueue->RemoveReader(); + } + Cancel(); + + if (iTransCoder) + { + if (iTranscoderStarted) + { + TRAPD(error, iTransCoder->StopL()); + if (error != KErrNone) { } + } + delete iTransCoder; + iTransCoder = 0; + } + + iFrameInfoArray.Reset(); + + // Close the decoder instance if one has been opened + if (iDecoder) + delete iDecoder; + iDecoder = 0; + + // Deallocate buffers + if (iDataBuffer) + User::Free(iDataBuffer); + + if (iOutVideoFrameBuffer) + User::Free(iOutVideoFrameBuffer); + + if (iFrameBuffer) + User::Free(iFrameBuffer); + + if ( iColorTransitionBuffer ) + User::Free( iColorTransitionBuffer ); + + if ( iOrigPreviousYUVBuffer ) + User::Free( iOrigPreviousYUVBuffer ); + + if (iMediaBuffer) + delete iMediaBuffer; + iMediaBuffer = 0; + + if (iDecoderSpecificInfo) + delete iDecoderSpecificInfo; + iDecoderSpecificInfo = 0; + + if (iOutputVolHeader) + delete iOutputVolHeader; + iOutputVolHeader = 0; + + if (iDelayedBuffer) + delete iDelayedBuffer; + iDelayedBuffer = 0; + + if (iMPEG4Timer) + delete iMPEG4Timer; + iMPEG4Timer = 0; + + if (iTimer) + delete iTimer; + iTimer = 0; + + if (iNotCodedFrame) + delete iNotCodedFrame; + iNotCodedFrame = 0; + +} + + +// --------------------------------------------------------- +// CVideoProcessor::ConstructL() +// Symbian 2nd phase constructor can leave. +// --------------------------------------------------------- +// +void CVideoProcessor::ConstructL() +{ + // Set as a reader to the input queue + iQueue->SetReader(this, NULL); + iReaderSet = ETrue; + + // Add us to active scheduler + CActiveScheduler::Add(this); + + iMediaBuffer = new (ELeave)CCMRMediaBuffer; + + // Allocate buffers + iDataBuffer = (TUint8*) User::AllocL(KInitialDataBufferSize); + iBufferLength = KInitialDataBufferSize; + + if ( iThumbnailMode ) + { + TSize a = iProcessor->GetMovieResolution(); + TInt length = a.iWidth*a.iHeight; + + length += (length>>1); + iFrameBuffer = (TUint8*)User::AllocL(length); + } + + TSize size(iVideoWidth, iVideoHeight); + + if (!iThumbnailMode) + { + // Open a decoder instance + iDecoder = CVedH263Dec::NewL(size, 1 /*iReferencePicturesNeeded*/); + + // create timer + iTimer = CCallbackTimer::NewL(*this); + } + + // Make us active + SetActive(); + iStatus = KRequestPending; +} + + +// --------------------------------------------------------- +// CVideoProcessor::Start +// Starts decoding +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::Start() +{ + if ( iDecoding ) + return; + + // Activate the object if we have data + if ( (!iDecodePending) && (iStatus == KRequestPending) && iQueue->NumDataBlocks() ) + { + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + } + + iDecoding = ETrue; +} + +// --------------------------------------------------------- +// CVideoProcessor::Stop +// Stops decoding +// (other items were commented in a header). +// --------------------------------------------------------- +// + +void CVideoProcessor::Stop() +{ + iDecoding = EFalse; + + if (iTimer) + iTimer->CancelTimer(); + + if (iTranscoderStarted) + { + TRAPD(error, iTransCoder->StopL()); + if (error != KErrNone) { } + iTranscoderStarted = EFalse; + } +} + +// --------------------------------------------------------- +// CVideoProcessor::RunL +// Standard active object running method +// (other items were commented in a header). +// --------------------------------------------------------- +// + +void CVideoProcessor::RunL() +{ + PRINT((_L("CVideoProcessor::RunL() in"))) + + // Don't decode if we aren't decoding + if (!iDecoding) + { + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + PRINT((_L("CVideoProcessor::RunL() out from !iDecoding branch"))) + return; + } + + if (iTranscoderInitPending) + { + iTranscoderInitPending = EFalse; + if (iStatus != KErrNone) + { + if (!iThumbnailMode) + { + VDASSERT(iMonitor, 101); + iMonitor->Error(iStatus.Int()); + } + else + { + iProcessor->NotifyThumbnailReady(iStatus.Int()); + } + + return; + } + // at this point we have already read a frame, + // so now start processing + iTransCoder->StartL(); + + // stop if a fatal error has occurred in starting + // the transcoder (decoding stopped in MtroFatalError) + if (!iDecoding) + return; + + iTranscoderStarted = ETrue; + + if (!iThumbnailMode) + { + ProcessFrameL(); + if (iDecodePending) + return; + } + else + { + ProcessThumb(ETrue); + return; + } + } + + if (iDecodePending) + { + iDecodePending = EFalse; + + if (iThumbnailMode) + { + if (iThumbDecoded) + { + PRINT((_L("CVideoProcessor::RunL() - thumb decoded"))) + ProcessThumb(EFalse); + } + else + { + PRINT((_L("CVideoProcessor::RunL() - thumb not decoded"))) + ReadAndWriteThumbFrame(); + } + return; + } + } + + if (iProcessingComplete) + { + PRINT((_L("CVideoProcessor::RunL() iProcessingComplete == ETrue"))) + VDASSERT(iMonitor, 102); + iMonitor->ClipProcessed(); + return; + } + + if (iFirstColorTransitionFrame) + { + Process2ndColorTransitionFrameL(); + return; + } + + while (!iDecodePending && !iDelayedWrite && !iTranscoderInitPending && + ReadFrame() ) + { + // process it + if ( ProcessFrameL() ) + { + // clip processed up until cut-out time, stop + if (iFrameInfoArray.Count()) + { + PRINT((_L("CVideoProcessor::RunL() - stream end reached, wait for frames"))); + iStreamEnd = iStreamEndRead = ETrue; + + // if there are still frames to be encoded, start timer + // since encoder may skip the rest of the frames + if ( IsNextFrameBeingEncoded() ) + { + PRINT((_L("CVideoProcessor::RunL(), set timer"))); + if ( !iTimer->IsPending() ) + iTimer->SetTimer( TTimeIntervalMicroSeconds32( iMaxEncodingDelay ) ); + } + return; + } + + iTimer->CancelTimer(); + if (iTranscoderStarted) + { + iTransCoder->StopL(); + iTranscoderStarted = EFalse; + } + VDASSERT(iMonitor, 103); + iMonitor->ClipProcessed(); + PRINT((_L("CVideoProcessor::RunL() out from ProcessFrameL == ETrue"))) + return; + } + } + + if ( !iDecodePending && !iDelayedWrite && !iTranscoderInitPending ) + { + + // We didn't get a frame + if (iStreamEnd) + { + iStreamEndRead = ETrue; + PRINT((_L("CVideoProcessor::RunL() - stream end reached"))); + if (iFrameInfoArray.Count()) + { + PRINT((_L("CVideoProcessor::RunL() - stream end reached, wait for frames"))); + // wait until frames have been processed + + // if there are still frames to be encoded, start timer + // since encoder may skip the rest of the frames + if ( IsNextFrameBeingEncoded() ) + { + PRINT((_L("CVideoProcessor::RunL(), set timer"))); + if ( !iTimer->IsPending() ) + iTimer->SetTimer( TTimeIntervalMicroSeconds32( iMaxEncodingDelay ) ); + } + return; + } + else + { + iTimer->CancelTimer(); + VDASSERT(iMonitor, 104); + iMonitor->ClipProcessed(); + } + } + else + { + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + } + } + + PRINT((_L("CVideoProcessor::RunL() out"))) + +} + +// ----------------------------------------------------------------------------- +// CVideoProcessor::RunError +// Called by the AO framework when RunL method has leaved +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CVideoProcessor::RunError(TInt aError) +{ + + if ((aError == CVedH263Dec::EDecoderNoIntra) || (aError == CVedH263Dec::EDecoderCorrupted)) + { + if (!iThumbnailMode) + iMonitor->Error(KErrCorrupt); + else + iProcessor->NotifyThumbnailReady(KErrCorrupt); + } + else + { + if (!iThumbnailMode) + iMonitor->Error(aError); + else + iProcessor->NotifyThumbnailReady(aError); + } + + return KErrNone; +} + + +// --------------------------------------------------------- +// CVideoProcessor::Process2ndColorTransitionFrameL +// Processes the second frame of a color transition double frame +// (other items were commented in a header). +// --------------------------------------------------------- +// +TBool CVideoProcessor::Process2ndColorTransitionFrameL() +{ + + TFrameInformation frameInfo; + frameInfo.iTranscoderMode = EFullWithIM; + frameInfo.iFrameNumber = iFrameNumber; + frameInfo.iEncodeFrame = ETrue; + frameInfo.iKeyFrame = EFalse; + frameInfo.iTransitionFrame = ETrue; + frameInfo.iTransitionPosition = EPositionStartOfClip; + frameInfo.iTransitionColor = iStartTransitionColor; + frameInfo.iTransitionFrameNumber = iTransitionFrameNumber; + frameInfo.iModificationApplied = EFalse; + frameInfo.iRepeatFrame = ETrue; + + TInt duration; + // get timestamp + iProcessor->GetNextFrameDuration(duration, frameInfo.iTimeStamp, iTimeStampIndex, iTimeStampOffset); + iTimeStampIndex++; + + frameInfo.iTimeStamp += iCutInTimeStamp; + + TTimeIntervalMicroSeconds ts = (iProcessor->GetVideoTimeInMsFromTicks(frameInfo.iTimeStamp, EFalse)) * 1000; + + if (ts <= iPreviousTimeStamp) + { + // adjust timestamp so that its bigger than ts of previous frame + TReal frameRate = iProcessor->GetVideoClipFrameRate(); + VDASSERT(frameRate > 0.0, 105); + TInt64 durationMs = TInt64( ( 1000.0 / frameRate ) + 0.5 ); + durationMs /= 2; // add half the duration of one frame + + ts = TTimeIntervalMicroSeconds( iPreviousTimeStamp.Int64() + durationMs*1000 ); + + frameInfo.iTimeStamp = iProcessor->GetVideoTimeInTicksFromMs( ts.Int64()/1000, EFalse ); + + ts = iProcessor->GetVideoTimeInMsFromTicks(frameInfo.iTimeStamp, EFalse) * 1000; + + PRINT((_L("CVideoProcessor::Process2ndColorTransitionFrameL() - adjusted timestamp, prev = %d, new = %d"), + I64INT( iPreviousTimeStamp.Int64() ) / 1000, I64INT( ts.Int64() ) / 1000)); + + } + + + iFrameInfoArray.Append(frameInfo); + + iPreviousTimeStamp = ts; + + iFirstColorTransitionFrame = EFalse; + + CCMRMediaBuffer::TBufferType bt = + (iDataFormat == EDataH263) ? CCMRMediaBuffer::EVideoH263 : CCMRMediaBuffer::EVideoMPEG4; + + if (!iNotCodedFrame) + GenerateNotCodedFrameL(); + + PRINT((_L("CVideoProcessor::Process2ndColorTransitionFrameL() - sending not coded"))); + +#ifdef VIDEOEDITORENGINE_AVC_EDITING + if (iDataFormat == EDataAVC) + { + TPtr8 ptr(iNotCodedFrame->Des()); + + TInt length = iNotCodedFrame->Length(); + iAvcEdit->ProcessAVCBitStreamL((TDes8&)(ptr), length, 0 /*dummy*/, EFalse ); + ptr.SetLength(length); + iDataLength = iCurrentFrameLength = length; + } +#endif + + iMediaBuffer->Set( TPtrC8(iNotCodedFrame->Des().Ptr(), iNotCodedFrame->Length()), + bt, + iNotCodedFrame->Length(), + EFalse, + ts ); + + iDecodePending = ETrue; + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + + PRINT((_L("CVideoProcessor::Process2ndColorTransitionFrameL() - WriteCodedBuffer, frame #%d, timestamp %d ms"), + iFrameNumber, I64INT( ts.Int64() ) / 1000 )); + iTransCoder->WriteCodedBufferL(iMediaBuffer); + + iFrameNumber++; + + return ETrue; + +} + + +// --------------------------------------------------------- +// CVideoProcessor::GenerateNotCodedFrameL +// Generate bitstream for not coded frame +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::GenerateNotCodedFrameL() +{ + + TSize resolution = iProcessor->GetVideoClipResolution(); + + if (iDataFormat == EDataH263) + { + // H.263 QCIF picture header + TInt headerSize = 7; + TUint8 notCodedH263[] = { 0x00, 0x00, 0x80, 0x02, 0x0a, 0x0c, 0x3f }; + TUint8 lsbMask[8] = { 255, 254, 252, 248, 240, 224, 192, 128 }; + + + if ( resolution == TSize(128,96) ) + notCodedH263[4] = 0x06; // set source format as sub-QCIF + + else if ( resolution == TSize(352, 288) ) + notCodedH263[4] = 0x0e; // set source format as CIF + + else if ( resolution != TSize(176,144) ) + User::Panic(_L("CVideoProcessor"), EInternalAssertionFailure); + + TInt numMBs = ( resolution.iWidth / 16 ) * ( resolution.iHeight / 16 ); + + // one COD bit for each MB, the last byte of the pic header already contains 6 MB bits + TInt bitsLeft = numMBs - 6; + + TInt bufSize = headerSize + ( bitsLeft / 8 ) + ( bitsLeft % 8 != 0 ); + + VDASSERT(!iNotCodedFrame, 117); + iNotCodedFrame = (HBufC8*) HBufC8::NewL(bufSize); + + TPtr8 buf(iNotCodedFrame->Des()); + buf.Copy(notCodedH263, headerSize); + + TInt index = headerSize; + TUint8* ptr = const_cast(buf.Ptr()); + + // set COD bit to 1 for all macroblocks + while (bitsLeft >= 8) + { + ptr[index++] = 0xff; + bitsLeft -= 8; + } + + if (bitsLeft) + { + TUint8 val = 0; + val |= lsbMask[8 - bitsLeft]; + ptr[index] = val; + } + buf.SetLength(bufSize); + } + + else if (iDataFormat == EDataMPEG4) + { + VDASSERT(iDataFormat == EDataMPEG4, 115); + + TUint8 vopStartCodeMPEG4[] = { 0x00, 0x00, 0x01, 0xb6 }; + + TInt headerSize = 4; + + // calculate the number of bits needed for vop_time_increment + TInt numTiBits; + for (numTiBits = 1; ((iInputTimeIncrementResolution - 1) >> numTiBits) != 0; numTiBits++) + { + } + + VDASSERT(numTiBits <= 16, 116); + + TInt numMBs = ( resolution.iWidth / 16 ) * ( resolution.iHeight / 16 ); + + // get VOP size + // vop_start_code: 32 + // vop_coding_type + modulo_time_base + marker_bit: 4 + // no. of bits for vop_time_increment + // marker_bit + vop_coded bit: 2 + // rounding_type: 1 + // intra_dc_vlc_thr: 3 + // vop_quant: 5 + // vop_fcode_forward: 3 + // not_coded for each MB: numMBs + TInt bufSizeBits = headerSize * 8 + 4 + numTiBits + 2 + 1 + 3 + 5 + 3 + numMBs;//DP mode not included! + if ( (iInputStreamMode == EVedVideoBitstreamModeMPEG4DP) + || (iInputStreamMode == EVedVideoBitstreamModeMPEG4DP_RVLC) + || (iInputStreamMode == EVedVideoBitstreamModeMPEG4Resyn_DP) + || (iInputStreamMode == EVedVideoBitstreamModeMPEG4Resyn_DP_RVLC) + ) + { + // Motion marker in DP mode + bufSizeBits+=17; + } + TInt bufSize = ( bufSizeBits / 8 ) + 1; // always 1-8 stuffing bits + + VDASSERT(!iNotCodedFrame, 118); + iNotCodedFrame = (HBufC8*) HBufC8::NewL(bufSize); + + TPtr8 buf(iNotCodedFrame->Des()); + buf.SetLength(bufSize); + buf.FillZ(); + buf.SetLength(0); + buf.Copy(vopStartCodeMPEG4, headerSize); + + TUint8* ptr = const_cast(buf.Ptr()); + TInt shift = 8; + TInt index = headerSize; + AddBits(ptr, shift, index, 1, 2); // vop_coding_type + AddBits(ptr, shift, index, 0, 1); // modulo_time_base + AddBits(ptr, shift, index, 1, 1); // marker_bit + + // vop_time_increment is left to zero (skip FillZ bits) + AddBits(ptr, shift, index, 0, numTiBits); + + // marker (1 bit; 1) + AddBits(ptr, shift, index, 1, 1); + // vop_coded (1 bit; 1=coded) + AddBits(ptr, shift, index, 1, 1); + + // vop_rounding_type (1 bit) (0) + AddBits(ptr, shift, index, 0, 1); + + // intra_dc_vlc_thr (3 bits) (0 = Intra DC, but don't care) + AddBits(ptr, shift, index, 0, 3); + + // vop_quant (5 bits) (1-31) + AddBits(ptr, shift, index, 10, 5); + + // vop_fcode_forward (3 bits) (1-7, 0 forbidden) + AddBits(ptr, shift, index, 1, 3); + + // Macroblocks + + // one COD bit for each MB + TInt bitsLeft = numMBs; + + // set COD bit to 1 for all macroblocks (== not coded) + while (bitsLeft >= 8) + { + AddBits(ptr, shift, index, 0xff, 8); + bitsLeft -= 8; + } + + TUint8 lsb[8] = { 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; + if (bitsLeft) + { + TUint8 val = 0; + val = lsb[8 - bitsLeft]; + AddBits(ptr, shift, index, val, bitsLeft); + } + // If DP mode is used, should add motion marker here: 1 11110000 00000001 + if ( (iInputStreamMode == EVedVideoBitstreamModeMPEG4DP) + || (iInputStreamMode == EVedVideoBitstreamModeMPEG4DP_RVLC) + || (iInputStreamMode == EVedVideoBitstreamModeMPEG4Resyn_DP) + || (iInputStreamMode == EVedVideoBitstreamModeMPEG4Resyn_DP_RVLC) + ) + { + AddBits(ptr, shift, index, 0x01, 1); + AddBits(ptr, shift, index, 0xf0, 8); + AddBits(ptr, shift, index, 0x01, 8); + } + + // insert stuffing in last byte + TUint8 stuffing[8] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f }; + ptr[bufSize - 1] |= stuffing[shift-1]; + + buf.SetLength(bufSize); + + } + else + { +#ifdef VIDEOEDITORENGINE_AVC_EDITING + + VDASSERT(iDataFormat == EDataAVC, 115); + + VDASSERT(!iNotCodedFrame, 118); + iNotCodedFrame = (HBufC8*) HBufC8::NewL(KAVCNotCodedFrameBuffer); + + TPtr8 buf( const_cast(iNotCodedFrame->Des().Ptr()), + KAVCNotCodedFrameBuffer, KAVCNotCodedFrameBuffer ); + + TInt len = iAvcEdit->GenerateNotCodedFrame( buf, iModifiedFrameNumber++ ); + + if (len == 0) + User::Leave(KErrArgument); + + TPtr8 temp(iNotCodedFrame->Des()); + temp.SetLength(len); +#else + VDASSERT(0, 190); +#endif + } + + +} + +// --------------------------------------------------------- +// CVideoProcessor::ProcessFrameL +// Processes one input frame +// (other items were commented in a header). +// --------------------------------------------------------- +// +TBool CVideoProcessor::ProcessFrameL() + { + + + PRINT((_L("CVideoProcessor::ProcessFrameL() begin"))); + + VDASSERT(iCurrentFrameLength,1); + + TInt frameInRange = 0; + TInt frameDuration = 0; + TBool keyFrame = EFalse; + TTimeIntervalMicroSeconds startCutTime = TTimeIntervalMicroSeconds(0); + TTimeIntervalMicroSeconds endCutTime = TTimeIntervalMicroSeconds(0); + TTimeIntervalMicroSeconds frameTime = TTimeIntervalMicroSeconds(0); + + TInt trP = iProcessor->GetTrPrevNew(); + TInt trD = iProcessor->GetTrPrevOrig(); + + // transitions + iTransitionFrame = 0; // is this a transition frame? + iTransitionPosition = EPositionNone; + iTransitionColor = EColorNone; + iFirstTransitionFrame = 0; // is this the first transition frame in this instance? + TBool endColorTransitionFrame = EFalse; + + TBool decodeCurrentFrame = 0; // do we need to decode frame for transition effect? + + // book-keeping + startCutTime = iProcessor->GetStartCutTime(); + endCutTime = iProcessor->GetEndCutTime(); + iRepeatFrame = EFalse; + + if(iInitializing) + { + + // determine if we need to do full transcoding + iFullTranscoding = DetermineResolutionChange() || DetermineFrameRateChange() || + DetermineBitRateChange(); + + // Do full transcoding for MPEG-4 => H.263. MPEG-4 => MPEG-4 and H.263 => MPEG-4 can be done in compressed domain + if ( iProcessor->GetCurrentClipVideoType() == EVedVideoTypeMPEG4SimpleProfile && + iProcessor->GetOutputVideoType() != EVedVideoTypeMPEG4SimpleProfile ) + iFullTranscoding = ETrue; + +#ifdef VIDEOEDITORENGINE_AVC_EDITING + // Do full transcoding for AVC => H.263/MPEG-4 + if ( iProcessor->GetCurrentClipVideoType() == EVedVideoTypeAVCBaselineProfile && + iProcessor->GetOutputVideoType() != EVedVideoTypeAVCBaselineProfile ) + iFullTranscoding = ETrue; + + // Do full transcoding for H.263/MPEG-4 => AVC + if ( iProcessor->GetCurrentClipVideoType() != EVedVideoTypeAVCBaselineProfile && + iProcessor->GetOutputVideoType() == EVedVideoTypeAVCBaselineProfile ) + iFullTranscoding = ETrue; + + // Do color effects for AVC in spatial domain + if ( iProcessor->GetOutputVideoType() == EVedVideoTypeAVCBaselineProfile && + iProcessor->GetColorEffect() != EVedColorEffectNone ) + iFullTranscoding = ETrue; +#endif + + // determine transition parameters for the clip + DetermineClipTransitionParameters(iTransitionEffect,iStartOfClipTransition, + iEndOfClipTransition,iStartTransitionColor,iEndTransitionColor); + + if (!iTransCoder) + { + // initialize transcoder, normal mode + CreateAndInitializeTranscoderL(iProcessor->GetCurrentClipVideoType(), CTRTranscoder::EFullTranscoding); + return EFalse; + } + + if ( iColorTransitionBuffer ) + { + User::Free( iColorTransitionBuffer ); + iColorTransitionBuffer = 0; + } + + if ( iOrigPreviousYUVBuffer ) + { + User::Free( iOrigPreviousYUVBuffer ); + iOrigPreviousYUVBuffer = 0; + } + + iFirstFrameInRange = 0; + iFirstIncludedFrameNumber = -1; + iTimeStampIndex = 0; + iTimeStampOffset = 0; + //iProcessor->iOutputFramesInClip=0; + iPreviousFrameIncluded = EFalse; + iNumberOfFrames = iProcessor->GetClipNumberOfFrames(); + + // calculate number of included frames + if(iTransitionEffect) + { + GetNumberOfTransitionFrames(startCutTime, endCutTime); + } + iInitializing = EFalse; + } + + TInt startFrame = iProcessor->GetOutputNumberOfFrames() - iNumberOfFrames; + TInt absFrameNumber = startFrame + iFrameNumber; + + VDASSERT(startCutTime <= endCutTime,2); + + // microseconds + frameTime = TTimeIntervalMicroSeconds(iProcessor->GetVideoTimeInMsFromTicks( + iProcessor->VideoFrameTimeStamp(absFrameNumber), EFalse) * 1000); + keyFrame = iProcessor->VideoFrameType(absFrameNumber); + + TInt cur = absFrameNumber; + TInt next = cur+1; + + TTimeIntervalMicroSeconds frameDurationInMicroSec(0); + + // frameDuration is in ticks, with timescale of the current input clip + if(next >= iProcessor->GetOutputNumberOfFrames()) + { + frameDurationInMicroSec = + (iProcessor->GetVideoTimeInMsFromTicks(iProcessor->GetVideoClipDuration(), EFalse) * TInt64(1000)) - frameTime.Int64(); + + frameDuration = I64INT(iProcessor->GetVideoClipDuration() - iProcessor->VideoFrameTimeStamp(cur) ); + } + else + { + frameDuration = I64INT( iProcessor->VideoFrameTimeStamp(next) - iProcessor->VideoFrameTimeStamp(cur) ); + frameDurationInMicroSec = + TTimeIntervalMicroSeconds(iProcessor->GetVideoTimeInMsFromTicks(TInt64(frameDuration), EFalse) * 1000); + } + + TTimeIntervalMicroSeconds frameEndTime = + TTimeIntervalMicroSeconds( frameTime.Int64() + frameDurationInMicroSec.Int64() ); + + // endCutTime is in TTimeIntervalMicroSeconds + + // check if frame is in range for decoding/editing + frameInRange = ((frameEndTime <= endCutTime) ? 1 : 0); + if(frameInRange) + { + + // transition is applied only for frame included in the output movie + if(frameTime >= startCutTime) + { + // find the offset for the first included frame in the clip + if(!iFirstFrameInRange) + { + iFirstFrameInRange = 1; + iFirstIncludedFrameNumber = iFrameNumber; + iModifiedFrameNumber = iFrameNumber + 1; // +1 since number is incremented after modifying + } + TInt relativeIncludedFrameNumber = iFrameNumber - iFirstIncludedFrameNumber; + + if(iTransitionEffect) + { + // check if this is a transition frame & set transition parameters + SetTransitionFrameParams(relativeIncludedFrameNumber, decodeCurrentFrame); + } + } + + // check if this is an end color transition frame + if ( iTransitionFrame && iTransitionPosition == EPositionEndOfClip && + iEndTransitionColor == EColorTransition ) + { + endColorTransitionFrame = ETrue; + iFrameToEncode = EFalse; + } + + // check if we need to include this frame into output movie + if (frameTime < startCutTime) + { + // decode, but do not include in output movie + // iPreviousFrameIncluded = EFalse; + iFrameToEncode = EFalse; + iFrameOperation = EDecodeNoWrite; + // for decoding frames not writable to output movie, do not decode + // with any effects, because all information is need at P->I conversion + } + else // include in output movie + { + + // check if we need to encode it again as I-frame + if (iFullTranscoding || (!iPreviousFrameIncluded && !keyFrame) || iTransitionFrame) + { + // need to decode as P and encode as I + + if (!endColorTransitionFrame) + iFrameToEncode = ETrue; + + iFrameOperation = EDecodeNoWrite; + // for first decoding of P frame in a clip, do not decode with any effects; + // instead, apply the effects in the spatial domain after decoding it as P; + // then feed it to the encoder with the applied special effects + } + else + { + +#ifdef VIDEOEDITORENGINE_AVC_EDITING + // check if we need to encode AVC frames after + // encoded cut frame or starting transition + if (iDataFormat == EDataAVC && iEncodeUntilIDR) + { + TPtr8 ptr(iDataBuffer, iCurrentFrameLength, iBufferLength); + + if (iAvcEdit->IsNALUnitIDR(ptr)) + iEncodeUntilIDR = 0; + else + { + // encode + iFrameOperation = EDecodeNoWrite; + if (!endColorTransitionFrame) + iFrameToEncode = ETrue; + } + } +#endif + + if (!iEncodeUntilIDR) + { + // just copy the frame data as it is + + TInt colorEffect = TColorEffect2TInt(iProcessor->GetColorEffect()); + + iFrameToEncode = EFalse; + if(decodeCurrentFrame) + iFrameOperation = EDecodeAndWrite; + else + iFrameOperation = (colorEffect==0/*None*/ ? EWriteNoDecode : EDecodeAndWrite); + } + } + iPreviousFrameIncluded = ETrue; + } + + } + else + { + // no need to include frame in output movie + iPreviousFrameIncluded = EFalse; + iFrameToEncode = EFalse; + iFrameOperation = ENoDecodeNoWrite; + + // stop processing + return ETrue; + } + + TBool modeChanged = GetModeChangeL(); // do we need to change the current mode? + + /* added to handle Mp4Specific size problem */ + if(modeChanged && !iFullTranscoding) + { + iProcessor->SetClipModeChanged(modeChanged); //if it is not set, it will be default false + } + + if (iFrameOperation == EDecodeAndWrite) + PRINT((_L("CVideoProcessor::ProcessFrameL() frame operation = EDecodeAndWrite"))); + if (iFrameOperation == EWriteNoDecode) + PRINT((_L("CVideoProcessor::ProcessFrameL() frame operation = EWriteNoDecode"))); + if (iFrameOperation == EDecodeNoWrite) + PRINT((_L("CVideoProcessor::ProcessFrameL() frame operation = EDecodeNoWrite"))); + if (iFrameOperation == ENoDecodeNoWrite) + PRINT((_L("CVideoProcessor::ProcessFrameL() frame operation = ENoDecodeNoWrite"))); + + PRINT((_L("CVideoProcessor::ProcessFrameL() iFrameToEncode = %d"), iFrameToEncode)); + + TBool volHeaderIncluded = EFalse; + + if( (iFrameOperation == EDecodeAndWrite) || (iFrameOperation == EWriteNoDecode) || + ((iFrameOperation == EDecodeNoWrite) && !iFullTranscoding && iFirstFrameFlag) ) + // the last line is to enable processing of the 1st frame also if it would be decoded with transcoder, + // to enable processing of the MPEG-4 VOL header by vedh263d. + { + + TPtr8 ptr(0,0); + TBool doCompressedDomainTC = modeChanged || iProcessor->GetColorEffect() != EVedColorEffectNone; + + // If we need to do compressed domain bitstream manipulation at some + // point of the clip, all frames must be decoded by vedh263d to be + // able to start bitstream modification in the middle of the clip, e.g. + // after a transition. If we are processing MPEG-4, all frames are + // manipulated by the decoder for changing timing information + + if ( doCompressedDomainTC || (iDataFormat == EDataMPEG4 /*&& !iTransitionFrame*/) ) + { + // use h263decoder to do bitstream modification + + // (if this is an end color transition frame, iFrameOperation is + // EDecodeNoWrite && iFrameToEncode == 0 + + TInt frameOp = 1; // EDecodeAndWrite + + if ( iFrameOperation == EDecodeNoWrite ) + frameOp = 2; + + if ( iFrameOperation == EWriteNoDecode && !modeChanged ) + frameOp = 3; // EWriteNoDecode + + TInt frameSize; + + if (iDataFormat == EDataMPEG4 && iFirstFrameFlag) + { + InsertDecoderSpecificInfoL(); + volHeaderIncluded = ETrue; + } + + // use h263decoder to do compressed domain transcoding + PRINT((_L("CVideoProcessor::ProcessFrameL() decode using vedh263d"))); + DecodeFrameL(frameOp, modeChanged, frameSize); + ptr.Set(iOutVideoFrameBuffer, frameSize, frameSize); + } + else + { + // copy bitstream directly + ptr.Set(iDataBuffer, iCurrentFrameLength, iCurrentFrameLength); + } + + if (iFrameOperation == EDecodeAndWrite || iFrameOperation == EWriteNoDecode) + { +#ifdef VIDEOEDITORENGINE_AVC_EDITING + if (iDataFormat == EDataAVC && iTransitionEffect && + iStartTransitionColor == EColorTransition) + { + if (!(iAvcEdit->IsNALUnitIDR(ptr))) + { + // modify frame number + VDASSERT( (iFrameNumber > iFirstIncludedFrameNumber), 182 ); + iAvcEdit->ModifyFrameNumber(ptr, iModifiedFrameNumber++); + PRINT((_L("CVideoProcessor::ProcessFrameL() modified frame no. => #%d"), iModifiedFrameNumber - 1)); + } + else + iModifiedFrameNumber = 1; // this frame is IDR, start numbering from zero + } +#endif + // Write to file + if ( WriteFrameToFileL(ptr, frameDuration, absFrameNumber) ) + return ETrue; + } + + if (iFrameOperation == EWriteNoDecode || + (iFrameOperation == EDecodeAndWrite && !decodeCurrentFrame && doCompressedDomainTC)) + { + // NOTE: The 2nd condition is for B&W & compr.domain TC + + // if we are doing only compressed domain transcoding, theres no need to + // decode the frame using transcoder + + // Throw away the data for this frame: + VDASSERT(iDataLength >= iCurrentFrameLength,4); + Mem::Copy(iDataBuffer, iDataBuffer + iCurrentFrameLength, + iDataLength - iCurrentFrameLength); + iDataLength = iDataLength - iCurrentFrameLength; + iCurrentFrameLength = 0; + + // update and fetch the new Time Code for MPEG4 ES + if (iProcessor->GetOutputVideoType() == EVedVideoTypeMPEG4SimpleProfile && + frameInRange && (frameTime >= startCutTime) ) + + { + iMPEG4Timer->UpdateMPEG4Time(absFrameNumber, iFrameNumber, iProcessor->GetSlowMotionSpeed()); + } + + iFrameNumber++; + return EFalse; + } + } + + // process using transcoder + if (iFrameOperation == EDecodeNoWrite || iFrameOperation == EDecodeAndWrite) + { + WriteFrameToTranscoderL(absFrameNumber, keyFrame, volHeaderIncluded); + } + + // update and fetch the new Time Code for MPEG4 ES + if (iProcessor->GetOutputVideoType() == EVedVideoTypeMPEG4SimpleProfile && + frameInRange && (frameTime >= startCutTime) ) + { + iMPEG4Timer->UpdateMPEG4Time(absFrameNumber, iFrameNumber, iProcessor->GetSlowMotionSpeed()); + } + + iProcessor->SetTrPrevNew(trP); + iProcessor->SetTrPrevOrig(trD); + + if (!iFirstColorTransitionFrame) + iFrameNumber++; + + PRINT((_L("CVideoProcessor::ProcessFrameL() end"))); + + return EFalse; +} + + +// --------------------------------------------------------- +// CVideoProcessor::WriteFrameToFileL +// Write frame to file +// (other items were commented in a header). +// --------------------------------------------------------- +// +TBool CVideoProcessor::WriteFrameToFileL(TPtr8& aBuf, TInt aDurationInTicks, TInt aFrameNumber) +{ + + // if there's a frame waiting to be encoded, we must not + // write this frame now. It will be written after all to-be-encoded + // frames have been written. New frames must not be processed + // until the delayed write has been completed + + iDelayedWrite = !IsEncodeQueueEmpty(); + + if (iDelayedWrite) + { + PRINT((_L("CVideoProcessor::WriteFrameToFileL() delayed write"))); + // save frame for later writing + if (iDelayedBuffer) + delete iDelayedBuffer; + iDelayedBuffer = 0; + + iDelayedBuffer = (HBufC8*) HBufC8::NewL(aBuf.Length()); + + TPtr8 db(iDelayedBuffer->Des()); + db.Copy(aBuf); + + iDelayedTimeStamp = iProcessor->VideoFrameTimeStamp(aFrameNumber) + iTimeStampOffset; + iDelayedKeyframe = iProcessor->VideoFrameType(aFrameNumber); + iDelayedFrameNumber = iFrameNumber; + if ( IsNextFrameBeingEncoded() ) + { + // start timer to wait for encoding to complete + if ( !iTimer->IsPending() ) + iTimer->SetTimer( TTimeIntervalMicroSeconds32( iMaxEncodingDelay ) ); + } + } + else + { + // write now + TInt error = iProcessor->WriteVideoFrameToFile(aBuf, + ( iProcessor->VideoFrameTimeStamp(aFrameNumber) + iTimeStampOffset ), + aDurationInTicks, iProcessor->VideoFrameType(aFrameNumber), EFalse, EFalse, EFalse ); + + // If movie has reached maximum size then stop processing + if (error == KErrCompletion) + { + iFrameInfoArray.Reset(); + return ETrue; + } + + // save frame number + iLastWrittenFrameNumber = iFrameNumber; + + User::LeaveIfError(error); + } + + return EFalse; + +} + + +// --------------------------------------------------------- +// CVideoProcessor::WriteFrameToTranscoderL +// Write frame to transcoder +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::WriteFrameToTranscoderL(TInt aFrameNumber, TBool aKeyFrame, TBool aVolHeaderInBuffer) +{ + + VDASSERT(iDataFormat != EDataUnknown, 30); + + // TODO: new buffertype for H.264 + CCMRMediaBuffer::TBufferType bt = + (iDataFormat == EDataH263) ? CCMRMediaBuffer::EVideoH263 : CCMRMediaBuffer::EVideoMPEG4; + + // insert dec. specific info header to beginning of buffer if it has not been sent + if (!iDecoderSpecificInfoSent && + ( iDataFormat == EDataAVC || (iDataFormat == EDataMPEG4 && !aVolHeaderInBuffer) ) ) + { + PRINT((_L("CVideoProcessor::WriteFrameToTranscoderL() - insert header"), iTranscoderMode)); + InsertDecoderSpecificInfoL(); + } + + // determine transcoder mode for this frame + TTranscoderMode mode; + + if (iFrameToEncode) + { + if ( iTransitionFrame || (iProcessor->GetColorEffect() != EVedColorEffectNone) ) + { + PRINT((_L("CVideoProcessor::WriteFrameToTranscoderL() encode current frame with intermediate modification"))); + mode = EFullWithIM; + } + else + { + PRINT((_L("CVideoProcessor::WriteFrameToTranscoderL() encode current frame"))); + mode = EFull; + } + } + else + { + PRINT((_L("CVideoProcessor::WriteFrameToTranscoderL() only decode current frame"))); + mode = EDecodeOnly; + } + + PRINT((_L("CVideoProcessor::WriteFrameToTranscoderL() iTranscoderMode %d"), iTranscoderMode)); + if (iTranscoderMode != mode) + { + if (mode == EDecodeOnly) + { + iTransCoder->EnableEncoder(EFalse); + iTransCoder->EnablePictureSink(ETrue); + } + else if (mode == EFull) + { + iTransCoder->EnableEncoder(ETrue); + iTransCoder->EnablePictureSink(EFalse); + } + else + { + iTransCoder->EnableEncoder(ETrue); + iTransCoder->EnablePictureSink(ETrue); + } + iTranscoderMode = mode; + } + + if (iFrameToEncode) + { + // should we encode an I-frame ? + if( (iTransitionFrame && iFirstTransitionFrame) || + (iFrameNumber == iFirstIncludedFrameNumber) || + iFirstFrameAfterTransition ) + { + iTransCoder->SetRandomAccessPoint(); + } + } + + TFrameInformation frameInfo; + frameInfo.iTranscoderMode = iTranscoderMode; + frameInfo.iFrameNumber = iFrameNumber; + frameInfo.iEncodeFrame = iFrameToEncode; + frameInfo.iKeyFrame = aKeyFrame; + + // this timestamp is in ticks for writing the frame + frameInfo.iTimeStamp = iProcessor->VideoFrameTimeStamp(aFrameNumber) + iTimeStampOffset; + frameInfo.iTransitionFrame = iTransitionFrame; + frameInfo.iTransitionPosition = iTransitionPosition; + frameInfo.iTransitionColor = iTransitionColor; + frameInfo.iTransitionFrameNumber = iTransitionFrameNumber; + frameInfo.iModificationApplied = EFalse; + + if(iTransitionFrame && iTransitionPosition == EPositionStartOfClip && + iStartTransitionColor == EColorTransition) + { + TInt duration; + TInt64 currentTimeStamp = iProcessor->VideoFrameTimeStamp(aFrameNumber); + + // get timestamp for 1st frame + iProcessor->GetNextFrameDuration(duration, frameInfo.iTimeStamp, iTimeStampIndex, iTimeStampOffset); + iTimeStampIndex++; + + if (iFirstTransitionFrame) + iCutInTimeStamp = currentTimeStamp; + + frameInfo.iTimeStamp += iCutInTimeStamp; + + // the duration parameter is not used actually, so no need to figure it out + iProcessor->AppendNextFrameDuration(duration, currentTimeStamp - iCutInTimeStamp); + +#ifdef VIDEOEDITORENGINE_AVC_EDITING + if (iDataFormat == EDataAVC) + { + + TPtr8 ptr(iDataBuffer, iCurrentFrameLength, iBufferLength); + if (!iDecoderSpecificInfoSent) + { + ptr.Set(iDataBuffer + iProcessor->GetDecoderSpecificInfoSize(), + iCurrentFrameLength - iProcessor->GetDecoderSpecificInfoSize(), iBufferLength); + } + + // Store PPS id + iAvcEdit->StoreCurrentPPSId( ptr ); + + if (iNotCodedFrame) + delete iNotCodedFrame; + iNotCodedFrame = 0; + } +#endif + + frameInfo.iRepeatFrame = EFalse; + iFirstColorTransitionFrame = ETrue; // to indicate that iDataBuffer must not be flushed + // in MtroReturnCodedBuffer + } + +#ifdef VIDEOEDITORENGINE_AVC_EDITING + if ( iDataFormat == EDataAVC && iTransitionEffect && iStartTransitionColor == EColorTransition && + (iFrameNumber > iFirstIncludedFrameNumber) && iFrameOperation != EDecodeAndWrite) + { + TPtr8 ptr(iDataBuffer, iCurrentFrameLength, iBufferLength); + + if (!iDecoderSpecificInfoSent) + { + ptr.Set(iDataBuffer + iProcessor->GetDecoderSpecificInfoSize(), + iCurrentFrameLength - iProcessor->GetDecoderSpecificInfoSize(), iBufferLength); + } + + if (!(iAvcEdit->IsNALUnitIDR(ptr))) + { + // modify frame number + iAvcEdit->ModifyFrameNumber(ptr, iModifiedFrameNumber++); + PRINT((_L("CVideoProcessor::WriteFrameToTranscoderL() modified frame no. => #%d"), iModifiedFrameNumber - 1)); + } + else + iModifiedFrameNumber = 1; // this frame is IDR, start numbering from zero + } +#endif + + // get timestamp in microseconds + TTimeIntervalMicroSeconds ts = + (iProcessor->GetVideoTimeInMsFromTicks(frameInfo.iTimeStamp, EFalse)) * 1000; + + if (ts <= iPreviousTimeStamp) + { + // adjust timestamp so that its bigger than ts of previous frame + TReal frameRate = iProcessor->GetVideoClipFrameRate(); + VDASSERT(frameRate > 0.0, 106); + TInt64 durationMs = TInt64( ( 1000.0 / frameRate ) + 0.5 ); + durationMs /= 2; // add half the duration of one frame + + ts = TTimeIntervalMicroSeconds( iPreviousTimeStamp.Int64() + durationMs*1000 ); + + frameInfo.iTimeStamp = iProcessor->GetVideoTimeInTicksFromMs( ts.Int64()/1000, EFalse ); + + ts = iProcessor->GetVideoTimeInMsFromTicks(frameInfo.iTimeStamp, EFalse) * 1000; + + PRINT((_L("CVideoProcessor::WriteFrameToTranscoderL() - adjusted timestamp, prev = %d, new = %d"), + I64INT( iPreviousTimeStamp.Int64() ) / 1000, I64INT( ts.Int64() ) / 1000)); + + } + + iFrameInfoArray.Append(frameInfo); + + iPreviousTimeStamp = ts; + +#ifdef VIDEOEDITORENGINE_AVC_EDITING + if (iDataFormat == EDataAVC) + { + TPtr8 ptr(iDataBuffer, iCurrentFrameLength, iBufferLength); + // This has to be updated when cutting from beginning ?? + iAvcEdit->ProcessAVCBitStreamL((TDes8&)(ptr), (TInt&)(iCurrentFrameLength), + iProcessor->GetDecoderSpecificInfoSize(), !iDecoderSpecificInfoSent ); + iDataLength = iCurrentFrameLength; + + } +#endif + + iMediaBuffer->Set( TPtrC8(iDataBuffer, iBufferLength), + bt, + iCurrentFrameLength, + aKeyFrame, + ts); + + iDecodePending = ETrue; + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + + + PRINT((_L("CVideoProcessor::WriteFrameToTranscoderL() - transcoder mode is %d"), iTranscoderMode)); + + PRINT((_L("CVideoProcessor::WriteFrameToTranscoderL() - WriteCodedBuffer, frame #%d, timestamp %d ms"), + iFrameNumber, I64INT( ts.Int64() ) / 1000 )); + + PRINT((_L("CVideoProcessor::WriteFrameToTranscoderL() - %d items in queue"), iFrameInfoArray.Count())); + + iTransCoder->WriteCodedBufferL(iMediaBuffer); + +} + + +// --------------------------------------------------------- +// CVideoProcessor::InsertDecoderSpecificInfoL +// Insert AVC dec. config record in the beginning of slice NAL('s) +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::InsertDecoderSpecificInfoL() +{ + + if (iDataFormat == EDataMPEG4) + { + if ( (iDataLength + iDecoderSpecificInfo->Length()) > iBufferLength ) + { + // extend buffer size + TUint newSize = iDataLength + iDecoderSpecificInfo->Length(); + iDataBuffer = (TUint8*) User::ReAllocL(iDataBuffer, newSize); + iBufferLength = newSize; + } + Mem::Copy(iDataBuffer+iDecoderSpecificInfo->Length(), iDataBuffer, iCurrentFrameLength); + Mem::Copy(iDataBuffer, iDecoderSpecificInfo->Des().Ptr(), iDecoderSpecificInfo->Length()); + iCurrentFrameLength += iDecoderSpecificInfo->Length(); + iDataLength += iDecoderSpecificInfo->Length(); + + return; + } + + VDASSERT( iDataFormat == EDataAVC, 182 ); + + // get number of slice NAL's in buffer + TInt frameLen = 0; + TInt numSliceNalUnits = 0; + TUint8* frameLenPtr = iDataBuffer; + + while (frameLen < iCurrentFrameLength) + { + TInt nalLen = 0; + + nalLen = (frameLenPtr[0] << 24) + (frameLenPtr[1] << 16) + + (frameLenPtr[2] << 8) + frameLenPtr[3] + 4; // +4 for length field + + frameLenPtr += nalLen; + frameLen += nalLen; + numSliceNalUnits++; + } + + // get no. of SPS & PPS + + TUint8* ptr = const_cast(iDecoderSpecificInfo->Des().Ptr()); + + TInt index = 4; // Skip version and length information + ptr[index] |= 0x03; // set no. bytes used for length to 4 + + index++; + TInt numSPS = ptr[index] & 0x1f; + + index++; + + // Loop all SPS units + for (TInt i = 0; i < numSPS; ++i) + { + TInt SPSSize = (ptr[index] << 8) + ptr[index + 1]; + index += 2; + index += SPSSize; + } + TInt numPPS = ptr[index]; + + // Align at 32-bit boundrary + TInt payLoadLen = iCurrentFrameLength + iDecoderSpecificInfo->Length(); + TInt alignmentBytes = (payLoadLen % 4 != 0) * ( 4 - (payLoadLen % 4) ); + + // get needed buffer length + TInt minBufLen = iCurrentFrameLength + iDecoderSpecificInfo->Length() + alignmentBytes + + ( (numSliceNalUnits + numSPS + numPPS) * 8 ) + 4; + + // ReAllocate buffer + if (iBufferLength < minBufLen) + { + iDataBuffer = (TUint8*) User::ReAllocL(iDataBuffer, minBufLen); + iBufferLength = minBufLen; + + PRINT((_L("CVideoProcessor::XXX() reallocated databuffer, new length = %d"),iBufferLength)); + } + + // move slice NAL's the amount of DCR length + Mem:: Copy(iDataBuffer + iDecoderSpecificInfo->Length(), iDataBuffer, iCurrentFrameLength); + + // copy SPS/PPS data in the beginning + Mem:: Copy(iDataBuffer, iDecoderSpecificInfo->Des().Ptr(), iDecoderSpecificInfo->Length()); + + iCurrentFrameLength += iDecoderSpecificInfo->Length(); + +} + +// --------------------------------------------------------- +// CVideoProcessor::GetModeChangeL +// Determine need to compr. domain transcoding +// (other items were commented in a header). +// --------------------------------------------------------- +// +TBool CVideoProcessor::GetModeChangeL() +{ + + TInt videoClipNumber = iProcessor->GetVideoClipNumber(); + + // iProcessor->GetModeTranslationMPEG4() returns the overall decision for inserted MPEG4 clips + + TVedTranscodeFactor tFact = iProcessor->GetVideoClipTranscodeFactor(videoClipNumber); + + TBool fModeChanged = EFalse; + + if (iProcessor->GetOutputVideoType() == EVedVideoTypeMPEG4SimpleProfile) //MPEG4 + { + + switch (tFact.iStreamType) + { + case EVedVideoBitstreamModeUnknown: + case EVedVideoBitstreamModeMPEG4Regular: + case EVedVideoBitstreamModeMPEG4Resyn: + fModeChanged = EFalse; // already the target mode + break; + case EVedVideoBitstreamModeH263: + fModeChanged = ETrue; + break; + case EVedVideoBitstreamModeMPEG4ShortHeader: + default: // other MPEG4 modes + // if all the MPEG4 (note: it is also considered as MPEG4 type) have the same mode + // no need to do the mode translation + fModeChanged = iProcessor->GetModeTranslationMpeg4() ? ETrue: EFalse; + break; + } + + } + else if ( (iProcessor->GetOutputVideoType() == EVedVideoTypeH263Profile0Level10) || + (iProcessor->GetOutputVideoType() == EVedVideoTypeH263Profile0Level45) ) + { + + if (tFact.iStreamType == EVedVideoBitstreamModeH263 || + tFact.iStreamType == EVedVideoBitstreamModeMPEG4ShortHeader|| + tFact.iStreamType ==EVedVideoBitstreamModeUnknown) + { + fModeChanged = EFalse; + } + else + { + fModeChanged = ETrue; + } + } + +#ifdef VIDEOEDITORENGINE_AVC_EDITING + else if (iProcessor->GetOutputVideoType() == EVedVideoTypeAVCBaselineProfile) + fModeChanged = EFalse; +#endif + + else // EVedVideoTypeNoVideo + { + User::Leave(KErrNotSupported); + } + + return fModeChanged; + +} + +// --------------------------------------------------------- +// CVideoProcessor::DecodeFrameL +// Decode frame in iDataBuffer using vedh263d +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::DecodeFrameL(TInt aOperation, TBool aModeChanged, TInt& aFrameSizeInBytes) +{ + + VDASSERT(iDataFormat == EDataH263 || iDataFormat == EDataMPEG4, 136); + + // go into the decoder + TVedColorEffect vedEffect = iProcessor->GetColorEffect(); + CVedH263Dec::TColorEffect effect = (vedEffect == EVedColorEffectNone) ? + CVedH263Dec::EColorEffectNone : ((vedEffect == EVedColorEffectBlackAndWhite) ? CVedH263Dec::EColorEffectBlackAndWhite : CVedH263Dec::EColorEffectToning); + + vdeDecodeParamters_t decodeParams; + + // Assign the ColorTone value of the ColorTone + // U,V value for the color toning + TInt colorToneU; + TInt colorToneV; + iProcessor->GetColorTone((TInt&)colorToneU, (TInt&)colorToneV); + + decodeParams.aColorToneU = colorToneU; + decodeParams.aColorToneV = colorToneV; + decodeParams.aColorEffect = effect; + decodeParams.aFrameOperation = aOperation; + decodeParams.aGetDecodedFrame = EFalse; //getDecodedFrame; // no need to get YUV + decodeParams.aSMSpeed = iProcessor->GetSlowMotionSpeed(); + + TInt trD = iProcessor->GetTrPrevOrig(); + TInt trP = iProcessor->GetTrPrevNew(); + + decodeParams.aTrD = &trD; + decodeParams.aTrP = &trP; + decodeParams.aVideoClipNumber = iProcessor->GetVideoClipNumber(); + + TVedTranscodeFactor tFact = iProcessor->GetVideoClipTranscodeFactor(decodeParams.aVideoClipNumber); + + decodeParams.streamMode = tFact.iStreamType; + decodeParams.iTimeIncrementResolution = tFact.iTRes; + decodeParams.aGetVideoMode = EFalse; + decodeParams.aOutputVideoFormat = iProcessor->GetOutputVideoType(); + + decodeParams.fModeChanged = aModeChanged; + decodeParams.fHaveDifferentModes = iProcessor->GetModeTranslationMpeg4() ? ETrue: EFalse; + /* Color Toning */ + decodeParams.aFirstFrameQp = iFirstFrameQp; + + // : Optimisation - If the frame is to be encoded, there is no need + // to process it using vedh263d in all cases, for example when + // doing end transition. In start transition case it has to be done + // so that compressed domain transcoding can continue after transition + + // before decoding, set the time infomation in the decoder parameters + decodeParams.aMPEG4TimeStamp = iMPEG4Timer->GetMPEG4TimeStampPtr(); + decodeParams.aMPEG4TargetTimeResolution = iMPEG4Timer->GetMPEG4TimeResolutionPtr(); + + decodeParams.vosHeaderSize = 0; + + // +3 includes the next PSC or EOS in the bit buffer + TPtrC8 inputPtr(iDataBuffer, iCurrentFrameLength + (iDataFormat==EDataH263 ? KH263StartCodeLength : KMPEG4StartCodeLength)); + + // check output buffer size & reallocate if its too small + if ( TReal(iOutVideoFrameBufferLength) < TReal(iCurrentFrameLength) * 1.5 ) + { + TInt newLen = TInt( TReal(iCurrentFrameLength) * 1.5 ); + + iOutVideoFrameBuffer = (TUint8*) User::ReAllocL(iOutVideoFrameBuffer, newLen); + iOutVideoFrameBufferLength = newLen; + + PRINT((_L("CVideoProcessor::DecodeFrameL() reallocated output buffer, new size = %d"), + iOutVideoFrameBufferLength)); + } + + TPtr8 outputPtr(iOutVideoFrameBuffer, 0, iOutVideoFrameBufferLength); + + TInt frameSize = 0; + TBool wasFirstFrame = iFirstFrameFlag; // need to save the value since it may be changed inside + iDecoder->DecodeFrameL(inputPtr, outputPtr, iFirstFrameFlag, frameSize, &decodeParams); + + if (frameSize > (TInt)iCurrentFrameLength) + { + // decoder used more data than was in the buffer => corrupted bitstream + PRINT((_L("CVideoProcessor::DecodeFrameL() decoder used more data than was in the buffer => corrupted bitstream"))); + User::Leave( KErrCorrupt ); + } + + aFrameSizeInBytes = outputPtr.Length(); + + /* record first frame QP */ + if ((iFrameNumber==0) && wasFirstFrame) + { + iFirstFrameQp = decodeParams.aFirstFrameQp; + + if (iProcessor->GetOutputVideoType() == EVedVideoTypeMPEG4SimpleProfile) + { + PRINT((_L("CVideoProcessor::DecodeFrameL() save VOS header, size %d"), decodeParams.vosHeaderSize)); + // sync the vol headers + if ( decodeParams.vosHeaderSize > iOutputVolHeader->Des().MaxLength() ) + { + delete iOutputVolHeader; + iOutputVolHeader = NULL; + iOutputVolHeader = HBufC8::NewL(decodeParams.vosHeaderSize); + } + iOutputVolHeader->Des().Copy( outputPtr.Ptr(), decodeParams.vosHeaderSize ); + } + } + + PRINT((_L("CVideoProcessor::DecodeFrameL() out"))); +} + +// --------------------------------------------------------- +// CVideoProcessor::CreateAndInitializeTranscoderL +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::CreateAndInitializeTranscoderL(TVedVideoType aInputType, CTRTranscoder::TTROperationalMode aMode) +{ + + PRINT((_L("CVideoProcessor::CreateAndInitializeTranscoderL() begin"))); + + VDASSERT(iTransCoder == 0, 27); + + iTransCoder = CTRTranscoder::NewL(*this); + + TBufC8<256> inputMime; + +// TInt volLength = 0; + TInt outputBufferSize = 0; + iInputMPEG4ProfileLevelId = 0; + + if (aInputType == EVedVideoTypeMPEG4SimpleProfile) + { + // Get the VOL header from the frame data + CVedVolReader* reader = CVedVolReader::NewL(); + CleanupStack::PushL(reader); + // Get pointer to the frame data + TPtrC8 inputPtr(iDecoderSpecificInfo->Des()); + reader->ParseVolHeaderL((TDesC8&) inputPtr); + + iInputMPEG4ProfileLevelId = reader->ProfileLevelId(); + + iInputTimeIncrementResolution = reader->TimeIncrementResolution(); + + iInputStreamMode = reader->BitstreamMode(); + + switch (iInputMPEG4ProfileLevelId) + { + case 8: + inputMime = _L8("video/mp4v-es; profile-level-id=8"); + outputBufferSize = KMaxCodedPictureSizeMPEG4QCIF / 2; + break; + + case 9: + inputMime = _L8("video/mp4v-es; profile-level-id=9"); + outputBufferSize = KMaxCodedPictureSizeMPEG4L0BQCIF / 2; + break; + + case 1: + inputMime = _L8("video/mp4v-es; profile-level-id=1"); + outputBufferSize = KMaxCodedPictureSizeMPEG4QCIF / 2; + break; + + case 2: + inputMime = _L8("video/mp4v-es; profile-level-id=2"); + outputBufferSize = KMaxCodedPictureSizeMPEG4CIF / 2; + break; + + case 3: + inputMime = _L8("video/mp4v-es; profile-level-id=3"); + outputBufferSize = KMaxCodedPictureSizeMPEG4CIF / 2; + break; + + case 4: + inputMime = _L8("video/mp4v-es; profile-level-id=4"); + outputBufferSize = KMaxCodedPictureSizeVGA / 2; + break; + + default: + inputMime = _L8("video/mp4v-es; profile-level-id=8"); + outputBufferSize = KMaxCodedPictureSizeMPEG4QCIF / 2; + break; + } + +// volLength = reader->HeaderSize(); + CleanupStack::PopAndDestroy(reader); + } + + else if (aInputType == EVedVideoTypeH263Profile0Level10) + { + inputMime = _L8("video/H263-2000; profile=0; level=10"); + outputBufferSize = KMaxCodedPictureSizeQCIF / 2; + } + + else if (aInputType == EVedVideoTypeH263Profile0Level45) + { + inputMime = _L8("video/H263-2000; profile=0; level=45"); + outputBufferSize = KMaxCodedPictureSizeQCIF / 2; + } + + else if (aInputType == EVedVideoTypeAVCBaselineProfile) + { + // get input avc level + VDASSERT( iAvcEdit != 0, 181 ); + VDASSERT( iDecoderSpecificInfo, 181 ); + + TPtr8 info = iDecoderSpecificInfo->Des(); + User::LeaveIfError( iAvcEdit->GetLevel(info, iInputAVCLevel) ); + + switch (iInputAVCLevel) + { + case 10: + inputMime = _L8("video/H264; profile-level-id=42800A"); + outputBufferSize = KMaxCodedPictureSizeAVCLevel1 / 2; + break; + + case 101: + inputMime = _L8("video/H264; profile-level-id=42900B"); + outputBufferSize = KMaxCodedPictureSizeAVCLevel1B / 2; + break; + + case 11: + inputMime = _L8("video/H264; profile-level-id=42800B"); + outputBufferSize = KMaxCodedPictureSizeAVCLevel1_1 / 2; + break; + + case 12: + inputMime = _L8("video/H264; profile-level-id=42800C"); + outputBufferSize = KMaxCodedPictureSizeAVCLevel1_2 / 2; + break; + + // NOTE: Levels 1.3 and 2 are enabled for testing purposes, + // to be removed + case 13: + inputMime = _L8("video/H264; profile-level-id=42800D"); + outputBufferSize = KMaxCodedPictureSizeAVCLevel1_3 / 2; + break; + + case 20: + inputMime = _L8("video/H264; profile-level-id=428014"); + outputBufferSize = KMaxCodedPictureSizeAVCLevel2 / 2; + break; + + //WVGA task + case 21: + inputMime = _L8("video/H264; profile-level-id=428015"); + outputBufferSize = KMaxCodedPictureSizeAVCLevel2_1 / 2; + break; + + case 22: + inputMime = _L8("video/H264; profile-level-id=428016"); + outputBufferSize = KMaxCodedPictureSizeAVCLevel2_2 / 2; + break; + + case 30: + inputMime = _L8("video/H264; profile-level-id=42801E"); + outputBufferSize = KMaxCodedPictureSizeAVCLevel3 / 2; + break; + + case 31: + inputMime = _L8("video/H264; profile-level-id=42801F"); + outputBufferSize = KMaxCodedPictureSizeAVCLevel3_1 / 2; + break; + + default: + User::Leave(KErrNotSupported); + break; + } + } + + else + User::Leave(KErrNotSupported); + + if ( !(iTransCoder->SupportsInputVideoFormat(inputMime) ) ) + { + User::Leave(KErrNotSupported); + } + + + // default framerate is 15 fps + TReal frameRate = 15.0; + + iOutputBitRate = 64000; + + if ( aMode == CTRTranscoder::EFullTranscoding ) + { + // get output mime type + SetOutputVideoCodecL(iProcessor->GetOutputVideoMimeType()); + + if ( !(iTransCoder->SupportsOutputVideoFormat(iOutputMimeType) ) ) + { + User::Leave(KErrNotSupported); + } + + // check output resolution + TSize outputResolution = iProcessor->GetMovieResolution(); + + if ( (outputResolution.iWidth > iMaxOutputResolution.iWidth) || (outputResolution.iHeight > iMaxOutputResolution.iHeight)) + { + if ( iArbitrarySizeAllowed ) // This is for future-proofness. Currently the checking of standard sizes below overrules this one + { + if ( outputResolution.iWidth * outputResolution.iHeight > iMaxOutputResolution.iWidth*iMaxOutputResolution.iHeight ) + { + PRINT((_L("CVideoProcessor::CreateAndInitializeTranscoderL() too high resolution requested"))); + User::Leave( KErrNotSupported ); + } + } + else + { + PRINT((_L("CVideoProcessor::CreateAndInitializeTranscoderL() incompatible or too high resolution requested"))); + User::Leave( KErrNotSupported ); + } + } + + // check size. For now only standard sizes are allowed + if ( (outputResolution != KVedResolutionSubQCIF) && + (outputResolution != KVedResolutionQCIF) && + (outputResolution != KVedResolutionCIF) && + (outputResolution != KVedResolutionQVGA) && + (outputResolution != KVedResolutionVGA16By9) && + (outputResolution != KVedResolutionVGA) && + //WVGA task + (outputResolution != KVedResolutionWVGA) ) + { + User::Leave( KErrArgument ); + } + + // check output frame rate + TReal movieFrameRate = iProcessor->GetMovieFrameRate(); + + if ( movieFrameRate > 0.0 ) + { + if ( movieFrameRate <= iMaxOutputFrameRate ) + { + frameRate = TReal32(movieFrameRate); + } + else + { + frameRate = iMaxOutputFrameRate; + } + } + + // check output bitrate + TInt movieBitRate = iProcessor->GetMovieVideoBitrate(); + TInt standardBitRate = iProcessor->GetMovieStandardVideoBitrate(); + + if ( movieBitRate > 0 ) + { + if ( movieBitRate <= iMaxOutputBitRate ) + { + iOutputBitRate = movieBitRate; + } + else + { + iOutputBitRate = iMaxOutputBitRate; + } + } + else if ( standardBitRate > 0 ) + { + if ( standardBitRate <= iMaxOutputBitRate ) + { + iOutputBitRate = standardBitRate; + } + else + { + iOutputBitRate = iMaxOutputBitRate; + } + } + } + else + { + iOutputMimeType = KNullDesC8; + } + + TTRVideoFormat videoInputFormat; + TTRVideoFormat videoOutputFormat; + + if (!iThumbnailMode) + { + videoInputFormat.iSize = iProcessor->GetVideoClipResolution(); + videoOutputFormat.iSize = iProcessor->GetMovieResolution(); + } + else + { + videoInputFormat.iSize = videoOutputFormat.iSize = TSize(iVideoWidth, iVideoHeight); + } + + videoInputFormat.iDataType = CTRTranscoder::ETRDuCodedPicture; + + if (aMode == CTRTranscoder::EFullTranscoding) + videoOutputFormat.iDataType = CTRTranscoder::ETRDuCodedPicture; + else + videoOutputFormat.iDataType = CTRTranscoder::ETRYuvRawData420; + + + iTransCoder->OpenL( this, + aMode, + inputMime, + iOutputMimeType, + videoInputFormat, + videoOutputFormat, + EFalse ); + + + iTransCoder->SetVideoBitRateL(iOutputBitRate); + + if (!iThumbnailMode) + { + // check framerate: target framerate cannot be larger than source framerate + TReal inputFR = iProcessor->GetVideoClipFrameRate(); + if ( inputFR <= 15.0 ) + { + inputFR = 15.0; + } + else + { + inputFR = 30.0; + } + if (frameRate > inputFR) + frameRate = inputFR; + } + + iTransCoder->SetFrameRateL(frameRate); + iTransCoder->SetChannelBitErrorRateL(0.0); + + // dummy + TTRVideoCodingOptions codingOptions; + codingOptions.iSyncIntervalInPicture = iProcessor->GetSyncIntervalInPicture(); + codingOptions.iMinRandomAccessPeriodInSeconds = (TInt) (1.0 / iProcessor->GetRandomAccessRate()); + codingOptions.iDataPartitioning = EFalse; + codingOptions.iReversibleVLC = EFalse; + codingOptions.iHeaderExtension = 0; + + iTransCoder->SetVideoCodingOptionsL(codingOptions); + + TSize targetSize; + if (!iThumbnailMode) + targetSize = iProcessor->GetMovieResolution(); + else + targetSize = TSize(iVideoWidth, iVideoHeight); + + iTransCoder->SetVideoPictureSinkOptionsL(targetSize, this); + + iTransCoder->EnableEncoder(EFalse); + iTransCoder->EnablePictureSink(ETrue); + iTranscoderMode = EDecodeOnly; + + // set init. data + TPtrC8 initData; + if (aInputType == EVedVideoTypeMPEG4SimpleProfile || + aInputType == EVedVideoTypeAVCBaselineProfile) + { + initData.Set(iDecoderSpecificInfo->Des()); + } + else + initData.Set(iDataBuffer, iCurrentFrameLength); + + iDecoderSpecificInfoSent = EFalse; + + iTransCoder->SetDecoderInitDataL( initData ); + + if (!iThumbnailMode) + { + // allocate output bitstream buffer for processing with vedh263d + VDASSERT( outputBufferSize != 0, 52 ); + iOutVideoFrameBuffer = (TUint8*) User::AllocL(outputBufferSize); + iOutVideoFrameBufferLength = outputBufferSize; + } + + iTranscoderInitPending = ETrue; + + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + + + iTransCoder->InitializeL(); + + // 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)/frameRate); +#endif + + iMaxItemsInProcessingQueue = iTransCoder->GetMaxFramesInProcessing(); + + PRINT((_L("CVideoProcessor::CreateAndInitializeTranscoderL() end"))); +} + +// --------------------------------------------------------- +// CVideoProcessor::MtroInitializeComplete +// Called by transcoder to indicate init. completion +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::MtroInitializeComplete(TInt aError) +{ + + TInt error = aError; + TInt outputTimeIncrementResolution = KDefaultTimeIncrementResolution; + + PRINT((_L("CVideoProcessor::MtroInitializeComplete() error = %d"), aError)); + + if ( !iThumbnailMode && (aError == KErrNone) && + ( (iProcessor->GetOutputVideoType() == EVedVideoTypeMPEG4SimpleProfile) +#ifdef VIDEOEDITORENGINE_AVC_EDITING + || (iProcessor->GetOutputVideoType() == EVedVideoTypeAVCBaselineProfile) +#endif + ) ) + { + PRINT((_L("CVideoProcessor::MtroInitializeComplete() calling GetCodingStandardSpecificInitOutputLC"))); + + // get & save vol header from encoder + TRAP(error, + { + iOutputVolHeader = iTransCoder->GetCodingStandardSpecificInitOutputLC(); + CleanupStack::Pop(); + }); + + iOutputVolHeaderWritten = EFalse; + + if ( error == KErrNone ) + { + +#ifdef VIDEOEDITORENGINE_AVC_EDITING + if (iProcessor->GetOutputVideoType() == EVedVideoTypeAVCBaselineProfile) + { + // Check if we need to use encoder + // : Are there any other cases where encoder is used _ + if ( iFullTranscoding || iTransitionEffect || + iProcessor->GetStartCutTime() != TTimeIntervalMicroSeconds(0) ) + { + HBufC8* outputAVCHeader = 0; + // is the max. size of AVCDecoderConfigurationRecord known here ?? + TRAP(error, outputAVCHeader = (HBufC8*) HBufC8::NewL(16384)); + + if (error == KErrNone) + { + TPtr8 ptr = outputAVCHeader->Des(); + + // parse header & convert it to AVCDecoderConfigurationRecord -format + TRAP(error, iAvcEdit->ConvertAVCHeaderL(*iOutputVolHeader, ptr)); + + if (error == KErrNone) + { + TRAP(error, iAvcEdit->SaveAVCDecoderConfigurationRecordL(ptr, ETrue)); + } + } + if (outputAVCHeader) + delete outputAVCHeader; + } + + iEncodeUntilIDR = 0; + if ( iStartOfClipTransition != 0 || + iProcessor->GetStartCutTime() != TTimeIntervalMicroSeconds(0) ) + { + // we need to use encoder at the beginning, now determine + // if we have to encode frames after cut / transition until + // input frame is IDR + iEncodeUntilIDR = iAvcEdit->EncodeUntilIDR(); + } + } + else +#endif + { + + VDASSERT(iOutputVolHeader, 49); + + // get time increment resolution using vol reader + CVedVolReader* reader = NULL; + TRAP( error, reader = CVedVolReader::NewL() ); + + if ( error == KErrNone ) + { + TRAP( error, reader->ParseVolHeaderL( (TDesC8&) *iOutputVolHeader ) ); + if (error == KErrNone) + { + outputTimeIncrementResolution = reader->TimeIncrementResolution(); + } + delete reader; + } + } + + } + } + + if (error == KErrNone) + { + // create MPEG-4 timing instance + TRAP(error, iMPEG4Timer = CMPEG4Timer::NewL(iProcessor, outputTimeIncrementResolution)); + } + + // enable pausing + if ( ((iFullTranscoding) + || (iProcessor->GetStartCutTime() > 0)) + && (iStartOfClipTransition == 0) + && (iEndOfClipTransition == 0) ) + { + // safe to enable pausing during transcoding: + // only when doing full transcoding or cutting from the beginning, but not if transitions + // rules out e.g. thumbnails, and codec-free cases + iTransCoder->EnablePausing(ETrue); + } + + VDASSERT(iTranscoderInitPending, 28); + // complete request + TRequestStatus *status = &iStatus; + User::RequestComplete(status, error); + +} + +// --------------------------------------------------------- +// CVideoProcessor::MtroFatalError +// Called by transcoder to indicate a fatal error +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::MtroFatalError(TInt aError) +{ + PRINT((_L("CVideoProcessor::MtroFatalError() %d"), aError)); + + if (iFullTranscoding || iThumbnailMode || iTransitionEffect || (iProcessor->GetStartCutTime() > 0)) + { + // ok, this is fatal, continue the method + PRINT((_L("CVideoProcessor::MtroFatalError() transcoder is in use, this is fatal"))); + } + else + { + // transcoder not in use, ignore + PRINT((_L("CVideoProcessor::MtroFatalError() transcoder not in use, ignore"))); + return; + } + + // stop decoding + Stop(); + + if (!iThumbnailMode) + iMonitor->Error(aError); + else + iProcessor->NotifyThumbnailReady(aError); + +} + +// --------------------------------------------------------- +// CVideoProcessor::MtroSuspend +// Suspends processing +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::MtroSuspend() +{ + + PRINT((_L("CVideoProcessor::MtroSuspend()"))); + + if (iProcessingComplete || (!iProcessor->NeedTranscoderAnyMore())) + { + PRINT((_L("CVideoProcessor::MtroSuspend(), this clip done or no video at all to process any more, ignore"))); + return; + } + + Cancel(); + iDecoding = EFalse; + iDecodePending = EFalse; + iDecodingSuspended = EFalse; + + if (iTimer) + iTimer->CancelTimer(); + + iProcessor->SuspendProcessing(); + + // flush input queue + if (iBlock) + iQueue->ReturnBlock(iBlock); + iBlock = iQueue->ReadBlock(); + while (iBlock) + { + iQueue->ReturnBlock(iBlock); + iBlock = iQueue->ReadBlock(); + } + iBlockPos = 0; + + iTranscoderMode = EUndefined; // force to reset the mode when writing the next picture + +} + +// --------------------------------------------------------- +// CVideoProcessor::MtroResume +// Re-starts processing after pause +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::MtroResume() +{ + PRINT((_L("CVideoProcessor::MtroResume()"))); + + if (iProcessingComplete) + { + PRINT((_L("CVideoProcessor::MtroResume(), processing of this clip completed, continue"))); + // fake RunL with this flag + iDecoding = ETrue; + if (!IsActive()) + { + // Make us active + PRINT((_L("CVideoProcessor::MtroResume() set active"))); + SetActive(); + iStatus = KRequestPending; + } + } + + if (!iProcessor->NeedTranscoderAnyMore()) + { + PRINT((_L("CVideoProcessor::MtroResume(), no video to process any more, ignore"))); + return; + } + + // flush frame info array and cancel timer + if (iTimer) + iTimer->CancelTimer(); + iFrameInfoArray.Reset(); + + Start(); + + TInt error = iProcessor->ResumeProcessing(iFrameNumber, iLastWrittenFrameNumber); + if (error != KErrNone) + iMonitor->Error(error); + + iNumberOfFrames = iProcessor->GetClipNumberOfFrames(); + iPreviousTimeStamp = TTimeIntervalMicroSeconds(-1); + + iDataLength = iCurrentFrameLength = 0; + iDataFormat = EDataUnknown; + + iStreamEnd = iStreamEndRead = 0; + + // reset also delayed buffer; it will need to be anyway re-read + delete iDelayedBuffer; + iDelayedBuffer = 0; + iDelayedWrite = EFalse; + + PRINT((_L("CVideoProcessor::MtroResume() - iFrameNumber = %d"), iFrameNumber)); + + if (!IsActive()) + { + // Make us active + PRINT((_L("CVideoProcessor::MtroResume() set active"))); + SetActive(); + iStatus = KRequestPending; + } + + PRINT((_L("CVideoProcessor::MtroResume() out"))); + +} + +// --------------------------------------------------------- +// CVideoProcessor::MtroReturnCodedBuffer +// Called by transcoder to return bitstream buffer +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::MtroReturnCodedBuffer(CCMRMediaBuffer* aBuffer) +{ + + VDASSERT(aBuffer == iMediaBuffer, 29); + + iIsThumbFrameBeingCopied = EFalse; + iDecoderSpecificInfoSent = ETrue; + +#ifdef _DEBUG + TTimeIntervalMicroSeconds ts = aBuffer->TimeStamp(); + + PRINT((_L("CVideoProcessor::MtroReturnCodedBuffer() timeStamp = %d ms"), + I64INT( ts.Int64() ) / 1000 )); + +#endif + + if (!iFirstColorTransitionFrame) + { + // Throw away the data for this frame: + VDASSERT(iDataLength >= iCurrentFrameLength,4); + Mem::Copy(iDataBuffer, iDataBuffer + iCurrentFrameLength, + iDataLength - iCurrentFrameLength); + iDataLength = iDataLength - iCurrentFrameLength; + iCurrentFrameLength = 0; + } + + if (!iThumbnailMode) + { + + // check if the next frame in queue is waiting to be encoded + // and start timer to detect possible frameskip + if ( IsNextFrameBeingEncoded() ) + { + if ( !iTimer->IsPending() ) + iTimer->SetTimer( TTimeIntervalMicroSeconds32( iMaxEncodingDelay ) ); + } + + if (iFrameInfoArray.Count() >= iMaxItemsInProcessingQueue) + { + PRINT((_L("CVideoProcessor::MtroReturnCodedBuffer() - %d items in queue, suspend decoding"), + iFrameInfoArray.Count() )); + + iDecodingSuspended = ETrue; + + return; + } + + VDASSERT(IsActive(), 40); + VDASSERT(iDecodePending, 41); + + // complete request + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + } + else if (iDataFormat == EDataAVC) + { + + VDASSERT(IsActive(), 40); + VDASSERT(iDecodePending, 41); + + // NOTE: it would make sense to call AsyncStopL() here, + // but at least in WINSCW it didn't have any effect + //if (iThumbFramesToWrite == 0) + //{ + //iTransCoder->AsyncStopL(); + //iTranscoderStarted = EFalse; + //} + + if (iStatus == KRequestPending) + { + PRINT((_L("CVideoProcessor::MtroReturnCodedBuffer() - completing request"))); + // complete request + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + } + } +} + +// --------------------------------------------------------- +// CVideoProcessor::MtroSetInputFrameRate +// Called by transcoder to request inout framerate +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::MtroSetInputFrameRate(TReal& aRate) +{ + TReal rate = iProcessor->GetVideoClipFrameRate(); + + if ( rate <= 15.0 ) + { + rate = 15.0; + } + else + { + rate = 30.0; + } + + aRate = rate; +} + +// --------------------------------------------------------- +// CVideoProcessor::MtroAsyncStopComplete +// Called by transcoder after async. stop is complete +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::MtroAsyncStopComplete() +{ + PRINT((_L("CVideoProcessor::MtroAsyncStopComplete()"))); +} + +// --------------------------------------------------------- +// CVideoProcessor::MtroPictureFromTranscoder +// Called by transcoder to return a decoded picture +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::MtroPictureFromTranscoder(TTRVideoPicture* aPicture) +{ + + TTimeIntervalMicroSeconds decodedTs = aPicture->iTimestamp; + + PRINT((_L("CVideoProcessor::MtroPictureFromTranscoder(), timestamp %d ms"), + I64INT( decodedTs.Int64() ) / 1000 )); + + if (iThumbnailMode) + { + iThumbDecoded = ETrue; + + // handle thumbnail + HandleThumbnailFromTranscoder(aPicture); + + return; + } + + // search the decoded frame from list + TInt index; + for (index = 0; index < iFrameInfoArray.Count(); ) + { + PRINT((_L("CVideoProcessor::MtroPictureFromTranscoder(), checking frame with index %d"), index)); + + TTimeIntervalMicroSeconds ts = + (iProcessor->GetVideoTimeInMsFromTicks(iFrameInfoArray[index].iTimeStamp, EFalse)) * 1000; + + if (ts < decodedTs && ( (iFrameInfoArray[index].iEncodeFrame == EFalse) || + (iFrameInfoArray[index].iTranscoderMode == EFullWithIM && + iFrameInfoArray[index].iModificationApplied == 0) ) ) + { + // if there are decode-only or transcoding w/intermediate modification + // frames in the queue before this one, remove those + PRINT((_L("CVideoProcessor::MtroPictureFromTranscoder(), removing frame with timestamp %d ms"), + I64INT( ts.Int64() ) / 1000 )); + + iFrameInfoArray.Remove(index); + // don't increment index + continue; + } + + if (ts == decodedTs) + { + PRINT((_L("CVideoProcessor::MtroPictureFromTranscoder(), found decoded frame at index %d"), index)); + break; + } + + index++; + } + + // If decoded frame is unexpected, i.e. it is not found from book-keeping, + // or it is not an intermediate modification frame, return frame here + // and continue + if ( index >= iFrameInfoArray.Count() || + ( iFrameInfoArray[index].iEncodeFrame == 1 && + iFrameInfoArray[index].iTranscoderMode != EFullWithIM ) ) + { + PRINT((_L("CVideoProcessor::MtroPictureFromTranscoder(), unexpected decoded frame, iTranscoderMode %d"), iTranscoderMode)); + // send picture back to transcoder + TInt error = KErrNone; + TRAP( error, iTransCoder->SendPictureToTranscoderL(aPicture) ); + if ( error != KErrNone ) + { + iMonitor->Error(error); + } + return; + } + + if (iFrameInfoArray[index].iEncodeFrame == EFalse) + { + // handle decode-only frame + HandleDecodeOnlyFrameFromTranscoder(aPicture, index); + return; + } + + // check color effect + TInt colorEffect = TColorEffect2TInt( iProcessor->GetColorEffect() ); + if (colorEffect != 0/*None*/) + { + // U,V value for the color toning + TInt colorToneU; + TInt colorToneV; + iProcessor->GetColorTone((TInt&)colorToneU, (TInt&)colorToneV); + // apply color effect + ApplySpecialEffect( colorEffect, const_cast(aPicture->iRawData->Ptr()), colorToneU, colorToneV ); + + } + + if(iFrameInfoArray[index].iTransitionFrame == 1) + { + // apply transition to frame + HandleTransitionFrameFromTranscoder(aPicture, index); + } + + iFrameInfoArray[index].iModificationApplied = ETrue; + + // send picture back to transcoder for encoding + TInt error = KErrNone; + TRAP( error, iTransCoder->SendPictureToTranscoderL(aPicture) ); + if ( error != KErrNone ) + { + iMonitor->Error(error); + return; + } + + // check if the next frame is waiting to be encoded, set timer if so + if ( IsNextFrameBeingEncoded() ) + { + if ( !iTimer->IsPending() ) + iTimer->SetTimer( TTimeIntervalMicroSeconds32( iMaxEncodingDelay ) ); + } + +} + + +// --------------------------------------------------------- +// CVideoProcessor::HandleThumbnailFromTranscoder +// Handle thumbnail frame +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::HandleThumbnailFromTranscoder(TTRVideoPicture* aPicture) +{ + + TInt error = KErrNone; + + PRINT((_L("CVideoProcessor::HandleThumbnailFromTranscoder() begin"))); + + if (iProcessingComplete) + { + // if requested thumbnail has been done already, + // just release picture and return + PRINT((_L("CVideoProcessor::HandleThumbnailFromTranscoder(), thumb already finished, returning"))); + + TRAP( error, iTransCoder->SendPictureToTranscoderL(aPicture) ); + if ( error != KErrNone ) + { + iMonitor->Error(error); + return; + } + return; + } + + TInt yuvLength = iVideoWidth*iVideoHeight; + yuvLength += (yuvLength >> 1); + // copy to iFrameBuffer + Mem::Copy(iFrameBuffer, aPicture->iRawData->Ptr(), yuvLength); + + // release picture + TRAP( error, iTransCoder->SendPictureToTranscoderL(aPicture) ); + if ( error != KErrNone ) + { + iProcessor->NotifyThumbnailReady(error); + return; + } + + VDASSERT(iDecodePending, 33); + VDASSERT(IsActive(), 150); + + if (iStatus == KRequestPending) + { + + PRINT((_L("CVideoProcessor::HandleThumbnailFromTranscoder(), complete request"))); + // complete request + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + } + + PRINT((_L("CVideoProcessor::HandleThumbnailFromTranscoder() end"))); +} + +// --------------------------------------------------------- +// CVideoProcessor::HandleDecodeOnlyFrameFromTranscoder +// Handle decode-only frame +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::HandleDecodeOnlyFrameFromTranscoder(TTRVideoPicture* aPicture, TInt aIndex) +{ + + VDASSERT(iFrameInfoArray[aIndex].iTranscoderMode == EDecodeOnly, 43); + + if ( iFrameInfoArray[aIndex].iTransitionFrame && + iFrameInfoArray[aIndex].iTransitionPosition == EPositionEndOfClip ) + { + if ( iEndTransitionColor == EColorTransition ) + { + // Save decoded frame to file + + TSize a = iProcessor->GetMovieResolution(); + TInt yuvLength = a.iWidth*a.iHeight; + yuvLength += (yuvLength>>1); + TPtr8 ptr(0,0); + TUint8* tmpBuf=0; + + ptr.Set( *aPicture->iRawData ); + tmpBuf = const_cast(aPicture->iRawData->Ptr()); + + TInt colorEffect = TColorEffect2TInt( iProcessor->GetColorEffect() ); + if (colorEffect != 0 /*None*/) + { + // U,V value for the color toning + TInt colorToneU; + TInt colorToneV; + iProcessor->GetColorTone((TInt&)colorToneU, (TInt&)colorToneV); + // apply special effect + ApplySpecialEffect( colorEffect, tmpBuf, colorToneU, colorToneV ); + } + + TInt frameDuration = GetFrameDuration(iFrameInfoArray[aIndex].iFrameNumber); + + if (frameDuration <= 0) + { + TReal frameRate = iProcessor->GetVideoClipFrameRate(); + VDASSERT(frameRate > 0.0, 107); + TInt timeScale = iProcessor->GetVideoClipTimeScale(); + TInt64 durationMs = TInt64( ( 1000.0 / frameRate ) + 0.5 ); + + // in ticks + frameDuration = TInt( ( (TReal)durationMs * (TReal)timeScale / 1000.0 ) + 0.5 ); + } + + TInt error = iProcessor->SaveVideoFrameToFile( ptr, frameDuration, iFrameInfoArray[aIndex].iTimeStamp ); + if ( error != KErrNone ) + { + PRINT((_L("CVideoProcessor::HandleDecodeOnlyFrameFromTranscoder() - SaveVideoFrameToFile failed"))); + iMonitor->Error(error); + return; + } + } + } + + iFrameInfoArray.Remove(aIndex); + + PRINT((_L("CVideoProcessor::HandleDecodeOnlyFrameFromTranscoder() - removed decode only pic, %d items in queue"), + iFrameInfoArray.Count())); + + // release picture + TInt error = KErrNone; + TRAP( error, iTransCoder->SendPictureToTranscoderL(aPicture) ); + if ( error != KErrNone ) + { + iMonitor->Error(error); + return; + } + + if (iStreamEndRead && iFrameInfoArray.Count() == 0 ) + { + PRINT((_L("CVideoProcessor::HandleDecodeOnlyFrameFromTranscoder() - stream end read, no frames left"))); + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + iTimer->CancelTimer(); + iProcessingComplete = ETrue; + // activate object to end processing + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + return; + } + + if (iDecodingSuspended && !iStreamEndRead) + { + if (iFrameInfoArray.Count() < iMaxItemsInProcessingQueue && !iDelayedWrite) + { + PRINT((_L("CVideoProcessor::HandleDecodeOnlyFrameFromTranscoder() - Resume decoding"))); + iDecodingSuspended = EFalse; + // activate object to start decoding + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + return; + } + } + + // check if the next frame is waiting to be encoded, set timer if so + if ( IsNextFrameBeingEncoded() ) + { + if ( !iTimer->IsPending() ) + iTimer->SetTimer( TTimeIntervalMicroSeconds32( iMaxEncodingDelay ) ); + } + + return; + +} + + +// --------------------------------------------------------- +// CVideoProcessor::HandleTransitionFrameFromTranscoder +// Handle transition frame +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::HandleTransitionFrameFromTranscoder(TTRVideoPicture* aPicture, TInt aIndex) +{ + + // apply transition effect in spatial domain (to yuv data in encoder buffer) + if ( iFrameInfoArray[aIndex].iTransitionPosition == EPositionStartOfClip && + iStartTransitionColor == EColorTransition ) + { + // Do blending transition: wipe / crossfade + + TSize a = iProcessor->GetMovieResolution(); + TInt yuvLength = a.iWidth*a.iHeight; + yuvLength += (yuvLength>>1); + + if (iFrameInfoArray[aIndex].iRepeatFrame == 0) + { + if( !iColorTransitionBuffer ) + { + iColorTransitionBuffer = (TUint8*)User::Alloc( yuvLength ); + if (!iColorTransitionBuffer) + { + iMonitor->Error(KErrNoMemory); + return; + } + } + + if( !iOrigPreviousYUVBuffer ) + { + iOrigPreviousYUVBuffer = (TUint8*)User::Alloc( yuvLength ); + if (!iOrigPreviousYUVBuffer) + { + iMonitor->Error(KErrNoMemory); + return; + } + } + + TPtr8 ptr( iColorTransitionBuffer, 0, yuvLength ); + + if ( iProcessor->GetVideoFrameFromFile( ptr, yuvLength, iFrameDuration, iTimeStamp ) != KErrNone ) + //|| iFrameDuration == 0 || iTimeStamp == 0 ) + { + // failure in reading frame data from previous clip + // continue using the current frame data + Mem::Copy( iColorTransitionBuffer, aPicture->iRawData->Ptr(), yuvLength ); + } + else + { + // buffer frame from previous clip (read from file) + Mem::Copy( iOrigPreviousYUVBuffer, iColorTransitionBuffer, yuvLength ); + if ( iStartOfClipTransition == (TInt)EVedMiddleTransitionEffectCrossfade ) + { + // Target frame is the one read from file, iColorTransitionBuffer + ApplyBlendingTransitionEffect( iColorTransitionBuffer, const_cast(aPicture->iRawData->Ptr()), + 0 /* repeatFrame */, iFrameInfoArray[aIndex].iTransitionFrameNumber); + } + else + { + // Target frame is the one read from file, iColorTransitionBuffer + ApplySlidingTransitionEffect( iColorTransitionBuffer, const_cast(aPicture->iRawData->Ptr()), (TVedMiddleTransitionEffect)iStartOfClipTransition, + 0 /* repeatFrame */, iFrameInfoArray[aIndex].iTransitionFrameNumber); + } + // copy frame from edited buffer to transcoder buffer + Mem::Copy( const_cast(aPicture->iRawData->Ptr()), iColorTransitionBuffer, yuvLength ); + } + } + else + { + // repeatFrame + + if ( iStartOfClipTransition == (TInt)EVedMiddleTransitionEffectCrossfade ) + { + ApplyBlendingTransitionEffect( iOrigPreviousYUVBuffer, const_cast(aPicture->iRawData->Ptr()), + 1 /* repeatFrame */, iFrameInfoArray[aIndex].iTransitionFrameNumber); + } + else + { + ApplySlidingTransitionEffect( iOrigPreviousYUVBuffer, const_cast(aPicture->iRawData->Ptr()), (TVedMiddleTransitionEffect)iStartOfClipTransition, + 1 /* repeatFrame */, iFrameInfoArray[aIndex].iTransitionFrameNumber ); + } + // copy frame from edited buffer to transcoder buffer + Mem::Copy( const_cast(aPicture->iRawData->Ptr()), iOrigPreviousYUVBuffer, yuvLength ); + } + } + else + { + // apply transition effect in spatial domain (to yuv data in encoder buffer) + + // Do fading transition + ApplyFadingTransitionEffect(const_cast(aPicture->iRawData->Ptr()), iFrameInfoArray[aIndex].iTransitionPosition, iFrameInfoArray[aIndex].iTransitionColor, + iFrameInfoArray[aIndex].iTransitionFrameNumber); + } + +} + +// --------------------------------------------------------- +// CVideoProcessor::ProcessThumb +// Starts thumbnail generation +// (other items were commented in a header). +// --------------------------------------------------------- +// +TInt CVideoProcessor::ProcessThumb(MThumbnailObserver* aThumbObserver, TInt aFrameIndex, TInt aStartFrameIndex, TVedTranscodeFactor* aFactor) +{ + TInt error; +// TInt goodFrame = 0; +// TInt frameSkip = 10; +// TInt frameNumber = aStartFrameIndex; + TPtrC8 inputPtr; + TPtr8 outputPtr(0,0); + + iThumbObserver = aThumbObserver; + iThumbFrameIndex = aFrameIndex; + iThumbFrameNumber = aStartFrameIndex; + iFramesToSkip = 0; + iNumThumbFrameSkips = 0; + iPreviousTimeStamp = TTimeIntervalMicroSeconds(-1); + iProcessingComplete = EFalse; + + iThumbFramesToWrite = iProcessor->GetOutputNumberOfFrames() - iThumbFrameNumber; + + // get transcode factor to determine input stream type + TRAP(error, GetTranscodeFactorL(*aFactor)); + if (error != KErrNone) + return error; + + TVedVideoType inType; + if (aFactor->iStreamType == EVedVideoBitstreamModeH263) + inType = EVedVideoTypeH263Profile0Level10; + + else if (aFactor->iStreamType == EVedVideoBitstreamModeAVC) + inType = EVedVideoTypeAVCBaselineProfile; + + else + inType = EVedVideoTypeMPEG4SimpleProfile; + + if (aFactor->iStreamType == EVedVideoTypeUnrecognized || + aFactor->iStreamType == EVedVideoTypeNoVideo) + return KErrorCode; + + iDecoding = ETrue; + + // first frame is now read in iDataBuffer, initialize transcoder + TRAP(error, CreateAndInitializeTranscoderL(inType, CTRTranscoder::EDecoding)) + if (error != KErrNone) + return error; + + // wait for initialisation to complete => RunL + + return KErrNone; + +} + +// --------------------------------------------------------- +// CVideoProcessor::ProcessThumb +// Processes a thumbnail frame internally +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::ProcessThumb(TBool aFirstFrame) +{ + + PRINT((_L("CVideoProcessor::ProcessThumb() - begin()"))); + + iThumbDecoded = EFalse; + + if (aFirstFrame) + { + // frame read in iDataBuffer, decode + CCMRMediaBuffer::TBufferType bt = + (iDataFormat == EDataH263) ? CCMRMediaBuffer::EVideoH263 : CCMRMediaBuffer::EVideoMPEG4; + TInt index = iThumbFrameNumber - ( iProcessor->GetOutputNumberOfFrames() - + iProcessor->GetClipNumberOfFrames() ); + TTimeIntervalMicroSeconds ts = + TTimeIntervalMicroSeconds(iProcessor->GetVideoTimeInMsFromTicks(iProcessor->VideoFrameTimeStamp(index), ETrue) * TInt64(1000) ); + + // Get the AVC bit stream and add NAL headers + if(iDataFormat == EDataAVC) + { + TInt error = KErrNone; + + // insert dec.config. record in the beginning of buffer + TRAP( error, InsertDecoderSpecificInfoL() ); + if (error != KErrNone) + { + iProcessor->NotifyThumbnailReady(error); + return; + } + + PRINT((_L("CVideoProcessor::ProcessThumb() - ProcessAVCBitStream()"))); + + TPtr8 ptr(iDataBuffer, iCurrentFrameLength, iBufferLength); + TRAP( error, iAvcEdit->ProcessAVCBitStreamL((TDes8&)(ptr), (TInt&)(iCurrentFrameLength), + iProcessor->GetDecoderSpecificInfoSize(), ETrue ) ); + + if (error != KErrNone) + { + iProcessor->NotifyThumbnailReady(error); + return; + } + iDataLength = iCurrentFrameLength; + + } + + // insert VOL header to beginning of buffer + if (iDataFormat == EDataMPEG4) + { + TRAPD( error, InsertDecoderSpecificInfoL() ); + if (error != KErrNone) + { + iProcessor->NotifyThumbnailReady(error); + return; + } + } + + iMediaBuffer->Set( TPtrC8(iDataBuffer, iBufferLength), + bt, + iCurrentFrameLength, + ETrue, // keyFrame + ts + ); + + iPreviousTimeStamp = ts; + + iIsThumbFrameBeingCopied = ETrue; + iDecodePending = ETrue; + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + + PRINT((_L("CVideoProcessor::ProcessThumb() - WriteCodedBuffer, thumb frame #%d, timestamp %d ms"), + iThumbFrameNumber, I64INT( ts.Int64() ) / 1000 )); + + TRAPD( err, iTransCoder->WriteCodedBufferL(iMediaBuffer) ); + if (err != KErrNone) + { + // ready + FinalizeThumb(err); + return; + } + iThumbFramesToWrite--; + + return; + } + + if (iThumbFrameIndex == 0) + { + // ready + FinalizeThumb(KErrNone); + return; + } + + iThumbFrameNumber++; + + if (iDataFormat == EDataAVC) + { + // In AVC case, we have to stop decoding before the very last + // frames are decoded, since for some reason the transcoder/decoder + // does not decode those frames + + // get max number of buffered frames according to spec + TInt buffered = iAvcEdit->GetMaxAVCFrameBuffering(iInputAVCLevel, TSize(iVideoWidth, iVideoHeight)); + + if (iThumbFrameNumber > iProcessor->GetOutputNumberOfFrames() - 1 - buffered ) + { + // ready + FinalizeThumb(KErrNone); + return; + } + } + + if (iThumbFrameIndex < 0) + { + if (iFramesToSkip == 0) + { + PRINT((_L("CVideoProcessor::ProcessThumb() frameskip done %d times"), iNumThumbFrameSkips)); + + // limit the number of frame skip cycles to 3, because with + // near-black or near-white videos we may never find a good thumb. + // => max. 30 frames are decoded to get the thumb + + // check quality & frame skip cycles + if ( CheckFrameQuality(iFrameBuffer) || iNumThumbFrameSkips >= 3 ) + { + // quality ok or searched long enough, return + FinalizeThumb(KErrNone); + return; + } + iFramesToSkip = 10; + iNumThumbFrameSkips++; + } + else + iFramesToSkip--; + + // read new frame & decode + } + + if (iThumbFrameIndex > 0) + { + if (iThumbFrameNumber > iThumbFrameIndex) + { + // ready + FinalizeThumb(KErrNone); + return; + } + // read new frame & decode + } + + if (iIsThumbFrameBeingCopied) + { + PRINT((_L("CVideoProcessor::ProcessThumb() - thumb being copied, activate"))); + // Re-activate to wait for MtroReturnCodedBuffer + iDecodePending = ETrue; + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + } + else + { + PRINT((_L("CVideoProcessor::ProcessThumb() - read and write new"))); + // send new frame for decoding + ReadAndWriteThumbFrame(); + } + + PRINT((_L("CVideoProcessor::ProcessThumb() - end"))); + +} + +// --------------------------------------------------------- +// CVideoProcessor::ReadAndWriteThumbFrame +// Reads a new frame to input queue and sends it to transcoder +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::ReadAndWriteThumbFrame() +{ + + PRINT((_L("CVideoProcessor::ReadAndWriteThumbFrame() - begin, thumb frames to write %d"), + iThumbFramesToWrite)); + + TInt error = KErrNone; + + if ( iThumbFramesToWrite ) + { + // read new frame to input queue + if(iThumbFrameNumber < (iProcessor->GetOutputNumberOfFrames())) // do not read last frame (already read!) + { + CMP4Demux *demux = (CMP4Demux *)iProcessor->GetDemux(); + error = demux->ReadVideoFrames(1); + if (error != KErrNone) + { + FinalizeThumb(error); + return; + } + } + else + { + // no frames left, return + FinalizeThumb(KErrNone); + return; + } + + iDataLength = 0; + iCurrentFrameLength = 0; + iDataFormat = EDataUnknown; + + if (ReadFrame()) + { + // frame read in iDataBuffer, decode + CCMRMediaBuffer::TBufferType bt = + (iDataFormat == EDataH263) ? CCMRMediaBuffer::EVideoH263 : CCMRMediaBuffer::EVideoMPEG4; + + TInt index = iThumbFrameNumber - ( iProcessor->GetOutputNumberOfFrames() - + iProcessor->GetClipNumberOfFrames() ); + + TTimeIntervalMicroSeconds ts = + TTimeIntervalMicroSeconds(iProcessor->GetVideoTimeInMsFromTicks(iProcessor->VideoFrameTimeStamp(index), ETrue) * TInt64(1000) ); + + if (ts <= iPreviousTimeStamp) + { + // adjust timestamp so that its bigger than ts of previous frame + TReal frameRate = iProcessor->GetVideoClipFrameRate(); + VDASSERT(frameRate > 0.0, 108); + TInt64 durationMs = TInt64( ( 1000.0 / frameRate ) + 0.5 ); + durationMs /= 2; // add half the duration of one frame + + ts = TTimeIntervalMicroSeconds( iPreviousTimeStamp.Int64() + durationMs*1000 ); + } + + iPreviousTimeStamp = ts; + + // Get the AVC bit stream and add NAL headers + if(iDataFormat == EDataAVC) + { + TPtr8 ptr(iDataBuffer, iCurrentFrameLength, iBufferLength); + TRAPD( error, iAvcEdit->ProcessAVCBitStreamL((TDes8&)(ptr), (TInt&)(iCurrentFrameLength), + iProcessor->GetDecoderSpecificInfoSize(), EFalse ) ); + + if (error != KErrNone) + { + FinalizeThumb(error); + return; + } + iDataLength = iCurrentFrameLength; + } + + iMediaBuffer->Set( TPtrC8(iDataBuffer, iBufferLength), + bt, + iCurrentFrameLength, + iProcessor->GetVideoFrameType(index), + ts ); + + iIsThumbFrameBeingCopied = ETrue; + iDecodePending = ETrue; + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + + PRINT((_L("CVideoProcessor::ProcessThumb() - WriteCodedBuffer, thumb frame #%d, timestamp %d ms"), + iThumbFrameNumber, I64INT( ts.Int64() ) / 1000 )); + + TRAPD( err, iTransCoder->WriteCodedBufferL(iMediaBuffer) ); + if (err != KErrNone) + { + FinalizeThumb(err); + } + iThumbFramesToWrite--; + return; + } + } + + else + { + if (iDataFormat == EDataAVC) + { + PRINT((_L("CVideoProcessor::ReadAndWriteThumbFrame() - all frames written, wait for output"))); + // all necessary frames written to decoder, now wait for output frames + iDecodePending = ETrue; + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + } + else + { + FinalizeThumb(KErrNone); + } + } + + PRINT((_L("CVideoProcessor::ReadAndWriteThumbFrame() - end"))); +} + +// --------------------------------------------------------- +// CVideoProcessor::FinalizeThumb +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::FinalizeThumb(TInt aError) +{ + iProcessingComplete = ETrue; + if (iTranscoderStarted) + { + TRAPD( err, iTransCoder->StopL() ); + if (err != KErrNone) { } + iTranscoderStarted = EFalse; + } + iProcessor->NotifyThumbnailReady(aError); +} + +// --------------------------------------------------------- +// CVideoProcessor::FetchThumb +// For getting a pointer to YUV thumbnail frame +// (other items were commented in a header). +// --------------------------------------------------------- +// +TInt CVideoProcessor::FetchThumb(TUint8** aYUVDataPtr) +{ + *aYUVDataPtr = iFrameBuffer; + + return KErrNone; +} + + +// --------------------------------------------------------- +// CVideoProcessor::GetTranscodeFactorL +// Gets the transcode factor from the current clip +// (other items were commented in a header). +// --------------------------------------------------------- +// +TInt CVideoProcessor::GetTranscodeFactorL(TVedTranscodeFactor& aFactor) +{ + // start reading video frames + CMP4Demux *demux = (CMP4Demux *)iProcessor->GetDemux(); + TInt error = demux->ReadVideoFrames(1); + + if (error != KErrNone) + User::Leave(error); + + // seek to and decode first frame + if (!ReadFrame()) + User::Leave(KErrCorrupt); + + // Get pointer to the frame data + TPtr8 inputPtr(0,0); + if ( iDataFormat == EDataH263 ) + inputPtr.Set(iDataBuffer, iCurrentFrameLength + KH263StartCodeLength, iCurrentFrameLength + KH263StartCodeLength); + else + inputPtr.Set(iDecoderSpecificInfo->Des()); + + if(iDataFormat == EDataAVC) + { + // @@ HARI AVC harcode for now + // Set transcode factors + aFactor.iTRes = 30; + aFactor.iStreamType = EVedVideoBitstreamModeAVC; + } + else + { + // Get the VOL header from the frame data + CVedVolReader* reader = CVedVolReader::NewL(); + CleanupStack::PushL(reader); + reader->ParseVolHeaderL((TDesC8&) inputPtr); + + // Set transcode factors + aFactor.iTRes = reader->TimeIncrementResolution(); + aFactor.iStreamType = reader->BitstreamMode(); + + CleanupStack::PopAndDestroy(reader); + } + + return KErrNone; +} + + + +// --------------------------------------------------------- +// CVideoProcessor::CheckFrameQuality +// Checks if a frame has "good" or "legible" quality +// (other items were commented in a header). +// --------------------------------------------------------- +// +TInt CVideoProcessor::CheckFrameQuality(TUint8* aYUVDataPtr) +{ + TInt i; + TInt minValue = 255; + TInt maxValue = 0; + TInt goodFrame = 1; + TInt runningSum=0; + TInt averageValue=0; + TInt pixelSkips = 4; + TInt numberOfSamples=0; + TInt minMaxDeltaThreshold = 20; + TInt extremeRegionThreshold = 20; + TInt ySize = iVideoWidth*iVideoHeight; + + // gather image statistics + for(i=0, numberOfSamples=0; i maxValue) + maxValue = *aYUVDataPtr; + if(*aYUVDataPtr < minValue) + minValue = *aYUVDataPtr; + } + VDASSERT(numberOfSamples,10); + averageValue = runningSum/numberOfSamples; + + // make decision based statistics + if((maxValue - minValue) < minMaxDeltaThreshold) + goodFrame = 0; + else + { + if(averageValue < (minValue + extremeRegionThreshold) || + averageValue > (maxValue - extremeRegionThreshold)) + goodFrame = 0; + } + return goodFrame; +} + + +// --------------------------------------------------------- +// CVideoProcessor::ReadFrame +// Reads a frame from input queue to internal buffer +// (other items were commented in a header). +// --------------------------------------------------------- +// +TInt CVideoProcessor::ReadFrame() +{ + + TUint doNow; + + if (iProcessor->GetCurrentClipVideoType() == EVedVideoTypeAVCBaselineProfile) + iDataFormat = EDataAVC; + + // Determine data format if needed + if ( iDataFormat == EDataUnknown ) + { + // We'll need four bytes of data + while ( iDataLength < 4 ) + { + // Get a block (we can't have one as we go here only at the stream + // start) + VDASSERT(!iBlock,11); + while ( (!iBlock) || (iBlock->Length() == 0) ) + { + if ( iBlock ) + iQueue->ReturnBlock(iBlock); + if ( (iBlock = iQueue->ReadBlock()) == NULL ) + return EFalse; + } + iBlockPos = 0; + + // get timestamp for first frame + if ( iTiming == ETimeStamp ) + { + VDASSERT( (TUint)iBlock->Length() >= 8,12 ); + iBlockPos += 4; + } + + // Copy data from block to buffer: + doNow = 4 - iDataLength; + if ( doNow > (TUint) iBlock->Length() - iBlockPos ) + doNow = iBlock->Length() - iBlockPos; + Mem::Copy(iDataBuffer+iDataLength, iBlock->Ptr()+iBlockPos, doNow); + iDataLength += doNow; + iBlockPos += doNow; + + // Return the block if it doesn't have any more data + if ( ((TInt)iBlockPos == iBlock->Length()) ) + { + iQueue->ReturnBlock(iBlock); + iBlock = 0; + } + } + + // OK, we have 4 bytes of data. Check if the buffer starts with a + // H.263 PSC: + if ( (iDataBuffer[0] == 0) && (iDataBuffer[1] == 0) && + ((iDataBuffer[2] & 0xfc) == 0x80) ) + { + // Yes, this is a H.263 stream + iDataFormat = EDataH263; + } + + // It should be MPEG-4, check if it starts with MPEG 4 Visual + // Object Sequence start code, Visual Object start code, Video + // Object start code, or Video Object Layer start code + else if ( ((iDataBuffer[0] == 0) && (iDataBuffer[1] == 0) && (iDataBuffer[2] == 1) && (iDataBuffer[3] == 0xb0)) || + ((iDataBuffer[0] == 0) && (iDataBuffer[1] == 0) && (iDataBuffer[2] == 1) && (iDataBuffer[3] == 0xb6)) || + ((iDataBuffer[0] == 0) && (iDataBuffer[1] == 0) && (iDataBuffer[2] == 1) && (iDataBuffer[3] == 0xb3)) || + ((iDataBuffer[0] == 0) && (iDataBuffer[1] == 0) && (iDataBuffer[2] == 1) && (iDataBuffer[3] == 0xb5)) || + ((iDataBuffer[0] == 0) && (iDataBuffer[1] == 0) && (iDataBuffer[2] == 1) && ((iDataBuffer[3] >> 5) == 0)) || + ((iDataBuffer[0] == 0) && (iDataBuffer[1] == 0) && (iDataBuffer[2] == 1) && ((iDataBuffer[3] >> 4) == 2))) + { + iDataFormat = EDataMPEG4; + } + else + { + PRINT((_L("CVideoProcessor::ReadFrame() - no PSC or MPEG-4 start code in the start of the buffer"))); + if (iMonitor) + iMonitor->Error(KErrCorrupt); + return EFalse; + } + } + + // Determine the start code length + TUint startCodeLength = 0; + switch (iDataFormat) + { + case EDataH263: + startCodeLength = KH263StartCodeLength; + break; + case EDataMPEG4: + startCodeLength = KMPEG4StartCodeLength ; + break; + case EDataAVC: + break; + + default: + User::Panic(_L("CVideoPlayer"), EInternalAssertionFailure); + } + + // If the stream has ended, we have no blocks and no data for even a + // picture start code, we can't get a frame + if( iDataFormat == EDataH263 ) + { + if ( iStreamEnd && (iQueue->NumDataBlocks() == 0) && + (iCurrentFrameLength <= startCodeLength) && (iDataLength <= startCodeLength) ) + return EFalse; + } + else + { + if ( iStreamEnd && (iQueue->NumDataBlocks() == 0) && + (iCurrentFrameLength <= startCodeLength) && (iDataLength < startCodeLength) ) + return EFalse; + } + + switch(iDataFormat) + { + case EDataH263: + return ReadH263Frame(); +// break; + + case EDataMPEG4: + return ReadMPEG4Frame(); +// break; + + case EDataAVC: + return ReadAVCFrame(); +// break; + + default: + User::Panic(_L("CVideoProcessor"), EInternalAssertionFailure); + + } + + return ETrue; +} + + +// --------------------------------------------------------- +// CVideoProcessor::ReadH263Frame +// Reads a H.263 frame from input queue to internal buffer +// (other items were commented in a header). +// --------------------------------------------------------- +// +TBool CVideoProcessor::ReadH263Frame() +{ + + VDASSERT( iDataFormat == EDataH263, 17 ); + + TInt offset = 0; + if ( iTiming == ETimeStamp ) + offset = 4; + + // There should be one PSC at the buffer start, + // and no other PSCs up to iDataLength + if ( (iDataLength >= KH263StartCodeLength) && + ((iDataBuffer[0] != 0) || (iDataBuffer[1] != 0) || ((iDataBuffer[2] & 0xfc) != 0x80)) ) + { + PRINT((_L("CVideoProcessor::ReadH263Frame() - no PSC in the start of the buffer"))) + if (iMonitor) + iMonitor->Error(KErrCorrupt); + return EFalse; + } + + if (iCurrentFrameLength < KH263StartCodeLength ) + iCurrentFrameLength = KH263StartCodeLength; + + TBool gotPSC = EFalse; + while (!gotPSC) + { + // If we don't have a block at the moment, get one and check if it + // has a new PSC + while (!iBlock) + { + if ((iBlock = iQueue->ReadBlock()) == NULL) + { + if (!iStreamEnd && !iProcessor->IsThumbnailInProgress()) + return EFalse; + + // No more blocks in the stream. If we have more data than + // just a PSC, use the remaining as the last frame. We'll + // append an End Of Stream (EOS) codeword to the stream end + // to keep the decoder happy + if (iDataLength <= 3) + return EFalse; + iCurrentFrameLength = iDataLength; + if (iBufferLength < (iDataLength+3)) + { + iBufferLength += 3; + TUint8* tmp = (TUint8*) User::ReAlloc(iDataBuffer, iBufferLength); + if ( !tmp ) + { + if (iMonitor) + iMonitor->Error(KErrNoMemory); + return EFalse; + } + iDataBuffer = tmp; + } + iDataBuffer[iCurrentFrameLength] = 0; + iDataBuffer[iCurrentFrameLength+1] = 0; + iDataBuffer[iCurrentFrameLength+2] = 0xfc; + iDataLength += 3; + return ETrue; + } + + iBlockPos = 0; + // Return empty blocks immediately + if ( iBlock->Length() == 0 ) + { + iQueue->ReturnBlock(iBlock); + iBlock = 0; + } + } + + // If we are at the start of a block, check if it begins with a PSC + if ( iBlockPos == 0 ) + { + if ( (iBlock->Length() > 2 + offset) && + ( ((*iBlock)[0+offset] == 0) && ((*iBlock)[1+offset] == 0) && (((*iBlock)[2+offset] & 0xfc) == 0x80) ) ) + { + gotPSC = ETrue; + iCurrentFrameLength = iDataLength; // timestamps not copied to buffer + + if (iTiming == ETimeStamp) + { + iBlockPos += offset; + } + } + else + { + PRINT((_L("CVideoProcessor::ReadH263Frame() - no PSC in the start of the buffer"))) + if (iMonitor) + iMonitor->Error( KErrCorrupt ); + return EFalse; + } + } + + // If we still have data in our current block, copy it to the buffer + // Make sure we have enough space + TUint copyBytes = iBlock->Length() - iBlockPos; + if (copyBytes) + { + while (iBufferLength < (iDataLength + copyBytes)) + { + // New size is 3/2ths of the old size, rounded up to the next + // full kilobyte + TUint newSize = (3 * iBufferLength) / 2; + newSize = (newSize + 1023) & (~1023); + + TUint8* tmp = (TUint8*) User::ReAlloc(iDataBuffer, newSize); + if (!tmp) + { + if (iMonitor) + iMonitor->Error(KErrNoMemory); + return EFalse; + } + iDataBuffer = tmp; + iBufferLength = newSize; + } + Mem::Copy(&iDataBuffer[iDataLength], iBlock->Ptr() + iBlockPos, + copyBytes); + iBlockPos += copyBytes; + iDataLength += copyBytes; + } + + // OK, block used, throw it away + VDASSERT(iBlock->Length() == (TInt)iBlockPos,16); + iQueue->ReturnBlock(iBlock); + iBlock = 0; + } + + return ETrue; +} + + + +// --------------------------------------------------------- +// CVideoProcessor::ReadMPEG4Frame +// Reads a MPEG-4 frame from input queue to internal buffer +// (other items were commented in a header). +// --------------------------------------------------------- +// +TBool CVideoProcessor::ReadMPEG4Frame() +{ + + VDASSERT( (iDataFormat == EDataMPEG4 && iTiming == ETimeStamp), 17 ); + + // The following code assumes that there is one complete video frame + // in each input block. This is true for 3GP input streams. + + // get a new block if we don't have one + while (!iBlock) + { + if ((iBlock = iQueue->ReadBlock()) == NULL) + return EFalse; + + iBlockPos = 0; + // Return empty blocks immediately + if (iBlock->Length() == 0) + { + iQueue->ReturnBlock(iBlock); + iBlock = 0; + } + } + + // If we are at the start of a block, save timestamp + if (iBlockPos == 0) + { + //TUint* p = (TUint*)iBlock->Ptr(); + //AI: iRenderFrameTime = TInt64( (TUint)((*p)*1000) ); + iBlockPos += 4; // skip timestamp + } + + if (iFirstRead) + { + // allocate buffer for header + VDASSERT(!iDecoderSpecificInfo, 160); + iDecoderSpecificInfo = (HBufC8*) HBufC8::New(iProcessor->GetDecoderSpecificInfoSize()); + if (!iDecoderSpecificInfo) + { + iMonitor->Error(KErrNoMemory); + return EFalse; + } + + TPtr8 ptr(iDecoderSpecificInfo->Des()); + + // first copy already read bytes from iDataBuffer + ptr.Copy(iDataBuffer, iDataLength); + + TInt copyNow = iProcessor->GetDecoderSpecificInfoSize() - iDataLength; + iDataLength = 0; + + // then copy the rest from input buffer + ptr.Append(iBlock->Ptr() + iBlockPos, copyNow); + iBlockPos += copyNow; + iDecoderSpecificInfoSent = EFalse; + + iFirstRead = EFalse; + + } + + TUint copyBytes = iBlock->Length() - iBlockPos; + if (copyBytes) + { + // Make sure we have enough space + // +4 is for inserting a start code at the end of the frame + while (iBufferLength < (iDataLength + copyBytes + 4)) + { + // New size is 3/2ths of the old size, rounded up to the next + // full kilobyte + TUint newSize = (3 * iBufferLength) / 2; + newSize = (newSize + 1023) & (~1023); + TUint8* tmp = (TUint8*) User::ReAlloc(iDataBuffer, newSize); + if (!tmp) + { + if (iMonitor) + iMonitor->Error(KErrNoMemory); + return EFalse; + } + iDataBuffer = tmp; + iBufferLength = newSize; + } + Mem::Copy(&iDataBuffer[iDataLength], iBlock->Ptr() + iBlockPos, + copyBytes); + iBlockPos += copyBytes; + iDataLength += copyBytes; + } + + // OK, block used, throw it away + VDASSERT((iBlock->Length() == (TInt)iBlockPos),18); + iQueue->ReturnBlock(iBlock); + iBlock = 0; + + // check for VOS end code + if ( (iDataBuffer[0] == 0 ) && (iDataBuffer[1] == 0 ) && + (iDataBuffer[2] == 0x01) && (iDataBuffer[3] == 0xb1) ) + return EFalse; + + // insert VOP start code at the end, the decoder needs it + iDataBuffer[iDataLength++] = 0; + iDataBuffer[iDataLength++] = 0; + iDataBuffer[iDataLength++] = 0x01; + iDataBuffer[iDataLength++] = 0xb6; + iCurrentFrameLength = iDataLength; + + // we have a complete frame + return ETrue; + +} + + +// --------------------------------------------------------- +// CVideoProcessor::ReadAVCFrame +// Reads an AVC frame from input queue to internal buffer +// (other items were commented in a header). +// --------------------------------------------------------- +// +TBool CVideoProcessor::ReadAVCFrame() +{ + + VDASSERT( iDataFormat == EDataAVC, 17 ); + + // get a new block if we don't have one + while (!iBlock) + { + if ((iBlock = iQueue->ReadBlock()) == NULL) + return EFalse; + + iBlockPos = 0; + // Return empty blocks immediately + if (iBlock->Length() == 0) + { + iQueue->ReturnBlock(iBlock); + iBlock = 0; + } + } + + // skip 4 bytes for the timestamp + TInt skip = 4; +// TInt numSPS = 0; +// TInt numPPS = 0; + + // set this to point to the start of frame length field + TUint8* frameLenPtr = const_cast(iBlock->Ptr()) + skip; + // how much space needed for frame data + TInt frameLen = 0; + + TInt totalFrameLen = iBlock->Length() - skip; + + if (iFirstRead) + { + TInt index = skip + 4; // Skip timestamp + version etc. + TUint8* temp = const_cast(iBlock->Ptr()); + + // get no. bytes used for length + iFrameLengthBytes = ( temp[index] & 0x3 ) + 1; + + // save DCR + VDASSERT(!iDecoderSpecificInfo, 160); + + iDecoderSpecificInfo = (HBufC8*) HBufC8::New(iProcessor->GetDecoderSpecificInfoSize()); + if (!iDecoderSpecificInfo) + { + iMonitor->Error(KErrNoMemory); + return EFalse; + } + + TPtr8 ptr(iDecoderSpecificInfo->Des()); + ptr.Copy(iBlock->Ptr() + skip, iProcessor->GetDecoderSpecificInfoSize()); + iDecoderSpecificInfoSent = EFalse; + + // advance pointer over info to point to length field + frameLenPtr += iProcessor->GetDecoderSpecificInfoSize(); + + // add to frame len. since it is used to calculate the minimum buffer size + //frameLen += iProcessor->GetDecoderSpecificInfoSize(); + + totalFrameLen -= iProcessor->GetDecoderSpecificInfoSize(); + skip += iProcessor->GetDecoderSpecificInfoSize(); + + iFirstRead = EFalse; + } + + + + TInt numSliceNalUnits = 0; + while (frameLen < totalFrameLen) + { + TInt nalLen = 0; + switch (iFrameLengthBytes) + { + case 1: + nalLen = frameLenPtr[0] + 1; // +1 for length field + break; + + case 2: + nalLen = (frameLenPtr[0] << 8) + frameLenPtr[1] + 2; // +2 for length field + break; + + case 3: + nalLen = (frameLenPtr[0] << 16) + (frameLenPtr[1] << 8) + + frameLenPtr[2] + 3; // +3 for length field + break; + + case 4: + nalLen = (frameLenPtr[0] << 24) + (frameLenPtr[1] << 16) + + (frameLenPtr[2] << 8) + frameLenPtr[3] + 4; // +4 for length field + break; + + default: + if (iMonitor) + iMonitor->Error(KErrCorrupt); + return EFalse; + } + frameLenPtr += nalLen; + frameLen += nalLen; + numSliceNalUnits++; + } + + if ( iFrameLengthBytes != 4 ) + frameLen += numSliceNalUnits * ( (iFrameLengthBytes == 1) ? 3 : 2 ); + + // reserve space for alignment + TInt addBytes = (frameLen % 4 != 0) * ( 4 - (frameLen % 4) ); + + // reserve space for slice NAL unit offset and size fields + addBytes += (numSliceNalUnits * 8); + + // reserve space for number of NAL units (4) + addBytes += 4; + + // Make sure we have enough space + while (iBufferLength < (iDataLength + frameLen + addBytes)) + { + // New size is 3/2ths of the old size, rounded up to the next + // full kilobyte + TUint newSize = (3 * iBufferLength) / 2; + newSize = (newSize + 1023) & (~1023); + TUint8* tmp = (TUint8*) User::ReAlloc(iDataBuffer, newSize); + if (!tmp) + { + iMonitor->Error(KErrNoMemory); + return EFalse; + } + + iDataBuffer = tmp; + iBufferLength = newSize; + } + + iBlockPos += skip; + + if (iFrameLengthBytes == 4) + { + // just copy directly, no need to change length field + Mem::Copy(&iDataBuffer[iDataLength], iBlock->Ptr() + skip, frameLen); + iDataLength += frameLen; + iBlockPos += frameLen; + } + else + { + // have to change length field for each NAL + TUint8* srcPtr = const_cast(iBlock->Ptr()) + skip; + while (numSliceNalUnits--) + { + // read length + TInt nalLen = 0; + switch (iFrameLengthBytes) + { + case 1: + nalLen = srcPtr[0]; + srcPtr += 1; // skip length field + iBlockPos += 1; + break; + + case 2: + nalLen = (srcPtr[0] << 8) + srcPtr[1]; + srcPtr += 2; // skip length field + iBlockPos += 2; + break; + + case 3: + nalLen = (srcPtr[0] << 16) + (srcPtr[1] << 8) + srcPtr[2]; + srcPtr += 3; // skip length field + iBlockPos += 3; + break; + + default: + if (iMonitor) + iMonitor->Error(KErrCorrupt); + return EFalse; + } + + // code length with 4 bytes + iDataBuffer[iDataLength] = TUint8((nalLen >> 24) & 0xff); + iDataBuffer[iDataLength + 1] = TUint8((nalLen >> 16) & 0xff); + iDataBuffer[iDataLength + 2] = TUint8((nalLen >> 8) & 0xff); + iDataBuffer[iDataLength + 3] = TUint8(nalLen & 0xff); + iDataLength += 4; + // copy NAL data + Mem::Copy(&iDataBuffer[iDataLength], srcPtr, nalLen); + iDataLength += nalLen; + srcPtr += nalLen; + iBlockPos += nalLen; + } + } + + // OK, block used, throw it away + VDASSERT((iBlock->Length() == (TInt)iBlockPos),18); + iQueue->ReturnBlock(iBlock); + iBlock = 0; + + iCurrentFrameLength = iDataLength; + + // we have a complete frame + return ETrue; + +} + + + +// --------------------------------------------------------- +// CVideoProcessor::DetermineClipTransitionParameters +// Sets transition frame parameters +// (other items were commented in a header). +// --------------------------------------------------------- +// +TInt CVideoProcessor::DetermineClipTransitionParameters(TInt& aTransitionEffect, + TInt& aStartOfClipTransition, + TInt& aEndOfClipTransition, + TTransitionColor& aStartTransitionColor, + TTransitionColor& aEndTransitionColor) +{ + TInt error=KErrNone; + // find if transition effect is to be applied + TInt numberOfVideoClips = iProcessor->GetNumberOfVideoClips(); + TInt videoClipNumber = iProcessor->GetVideoClipNumber(); + TVedStartTransitionEffect startTransitionEffect = iProcessor->GetStartTransitionEffect(); + TVedEndTransitionEffect endTransitionEffect = iProcessor->GetEndTransitionEffect(); + TVedMiddleTransitionEffect middleTransitionEffect = iProcessor->GetMiddleTransitionEffect(); + TVedMiddleTransitionEffect previousMiddleTransitionEffect = iProcessor->GetPreviousMiddleTransitionEffect(); + + // is transition effect to be applied anywhere in the movie? + if(startTransitionEffect==EVedStartTransitionEffectNone && + middleTransitionEffect==EVedMiddleTransitionEffectNone && + endTransitionEffect==EVedEndTransitionEffectNone && + previousMiddleTransitionEffect==EVedMiddleTransitionEffectNone) + aTransitionEffect=0; + else + aTransitionEffect=1; + // where is the transition effect to be applied - beginning, end or both? + if(aTransitionEffect) + { + // if first video clip + if(videoClipNumber==0) + { + switch(startTransitionEffect) + { + default: + case EVedStartTransitionEffectNone: + case EVedStartTransitionEffectLast: + aStartOfClipTransition=0; + aStartTransitionColor = EColorNone; + break; + case EVedStartTransitionEffectFadeFromBlack: + aStartOfClipTransition=1; + aStartTransitionColor = EColorBlack; + break; + case EVedStartTransitionEffectFadeFromWhite: + aStartOfClipTransition=1; + aStartTransitionColor = EColorWhite; + break; + } + // do we need transition at the end of this clip? + if(videoClipNumber==numberOfVideoClips-1) // last clip? + { + switch(endTransitionEffect) + { + default: + case EVedEndTransitionEffectNone: + case EVedEndTransitionEffectLast: + aEndOfClipTransition=0; + aEndTransitionColor = EColorNone; + break; + case EVedEndTransitionEffectFadeToBlack: + aEndOfClipTransition=1; + aEndTransitionColor = EColorBlack; + break; + case EVedEndTransitionEffectFadeToWhite: + aEndOfClipTransition=1; + aEndTransitionColor = EColorWhite; + break; + } + } + else // middle clip + { + switch(middleTransitionEffect) + { + default: + case EVedMiddleTransitionEffectNone: + case EVedMiddleTransitionEffectLast: + aEndOfClipTransition=0; + aEndTransitionColor = EColorNone; + break; + case EVedMiddleTransitionEffectDipToBlack: + aEndOfClipTransition=1; + aEndTransitionColor = EColorBlack; + break; + case EVedMiddleTransitionEffectDipToWhite: + aEndOfClipTransition=1; + aEndTransitionColor = EColorWhite; + break; + //change + case EVedMiddleTransitionEffectCrossfade: + aEndOfClipTransition=TInt(EVedMiddleTransitionEffectCrossfade); + aEndTransitionColor = EColorTransition; + break; + case EVedMiddleTransitionEffectWipeLeftToRight: + aEndOfClipTransition=TInt(EVedMiddleTransitionEffectWipeLeftToRight); + aEndTransitionColor = EColorTransition; + break; + case EVedMiddleTransitionEffectWipeRightToLeft: + aEndOfClipTransition=TInt(EVedMiddleTransitionEffectWipeRightToLeft); + aEndTransitionColor = EColorTransition; + break; + case EVedMiddleTransitionEffectWipeTopToBottom: + aEndOfClipTransition=TInt(EVedMiddleTransitionEffectWipeTopToBottom); + aEndTransitionColor = EColorTransition; + break; + case EVedMiddleTransitionEffectWipeBottomToTop: + aEndOfClipTransition=TInt(EVedMiddleTransitionEffectWipeBottomToTop); + aEndTransitionColor = EColorTransition; + break; + } + } + } + // else its the middle or last clip + else + { + // do we need transition at the beginning of this clip? + switch(previousMiddleTransitionEffect) + { + default: + case EVedMiddleTransitionEffectNone: + case EVedMiddleTransitionEffectLast: + aStartOfClipTransition=0; + aStartTransitionColor = EColorNone; + break; + case EVedMiddleTransitionEffectDipToBlack: + aStartOfClipTransition=1; + aStartTransitionColor = EColorBlack; + break; + case EVedMiddleTransitionEffectDipToWhite: + aStartOfClipTransition=1; + aStartTransitionColor = EColorWhite; + break; + + case EVedMiddleTransitionEffectCrossfade: + aStartOfClipTransition=TInt(EVedMiddleTransitionEffectCrossfade); + aStartTransitionColor = EColorTransition; + break; + case EVedMiddleTransitionEffectWipeLeftToRight: + aStartOfClipTransition=TInt(EVedMiddleTransitionEffectWipeLeftToRight); + aStartTransitionColor = EColorTransition; + break; + case EVedMiddleTransitionEffectWipeRightToLeft: + aStartOfClipTransition=TInt(EVedMiddleTransitionEffectWipeRightToLeft); + aStartTransitionColor = EColorTransition; + break; + case EVedMiddleTransitionEffectWipeTopToBottom: + aStartOfClipTransition=TInt(EVedMiddleTransitionEffectWipeTopToBottom); + aStartTransitionColor = EColorTransition; + break; + case EVedMiddleTransitionEffectWipeBottomToTop: + aStartOfClipTransition=TInt(EVedMiddleTransitionEffectWipeBottomToTop); + aStartTransitionColor = EColorTransition; + break; + } + // do we need transition at the end of this clip? + if(videoClipNumber==numberOfVideoClips-1) // last clip? + { + switch(endTransitionEffect) + { + default: + case EVedEndTransitionEffectNone: + case EVedEndTransitionEffectLast: + aEndOfClipTransition=0; + aEndTransitionColor = EColorNone; + break; + case EVedEndTransitionEffectFadeToBlack: + aEndOfClipTransition=1; + aEndTransitionColor = EColorBlack; + break; + case EVedEndTransitionEffectFadeToWhite: + aEndOfClipTransition=1; + aEndTransitionColor = EColorWhite; + break; + } + } + else // middle clip + { + switch(middleTransitionEffect) + { + default: + case EVedMiddleTransitionEffectNone: + case EVedMiddleTransitionEffectLast: + aEndOfClipTransition=0; + aEndTransitionColor = EColorNone; + break; + case EVedMiddleTransitionEffectDipToBlack: + aEndOfClipTransition=1; + aEndTransitionColor = EColorBlack; + break; + case EVedMiddleTransitionEffectDipToWhite: + aEndOfClipTransition=1; + aEndTransitionColor = EColorWhite; + break; + //change + case EVedMiddleTransitionEffectCrossfade: + aEndOfClipTransition=TInt(EVedMiddleTransitionEffectCrossfade); + aEndTransitionColor = EColorTransition; + break; + case EVedMiddleTransitionEffectWipeLeftToRight: + aEndOfClipTransition=TInt(EVedMiddleTransitionEffectWipeLeftToRight); + aEndTransitionColor = EColorTransition; + break; + case EVedMiddleTransitionEffectWipeRightToLeft: + aEndOfClipTransition=TInt(EVedMiddleTransitionEffectWipeRightToLeft); + aEndTransitionColor = EColorTransition; + break; + case EVedMiddleTransitionEffectWipeTopToBottom: + aEndOfClipTransition=TInt(EVedMiddleTransitionEffectWipeTopToBottom); + aEndTransitionColor = EColorTransition; + break; + case EVedMiddleTransitionEffectWipeBottomToTop: + aEndOfClipTransition=TInt(EVedMiddleTransitionEffectWipeBottomToTop); + aEndTransitionColor = EColorTransition; + break; + } + } + } + } + return error; +} + + +// --------------------------------------------------------- +// CVideoProcessor::GetNumberOfTransitionFrames +// Calculate the number of transition frames +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::GetNumberOfTransitionFrames(TTimeIntervalMicroSeconds aStartCutTime, + TTimeIntervalMicroSeconds aEndCutTime) +{ + + TInt startFrameIndex = iProcessor->GetVideoFrameIndex(aStartCutTime); + // the following is because binary search gives us frame with timestamp < startCutTime + // this frame would be out of range for movie + if(startFrameIndex > 0 && startFrameIndex < iNumberOfFrames-1) + startFrameIndex++; + TInt endFrameIndex = iProcessor->GetVideoFrameIndex(aEndCutTime); + // adjust frame indices for cut video clip + startFrameIndex -= iProcessor->GetStartFrameIndex(); + endFrameIndex -= iProcessor->GetStartFrameIndex(); + if(startFrameIndex < 0) + startFrameIndex = 0; + if(endFrameIndex < 0) + endFrameIndex = 0; + if(endFrameIndex>= 1; + + // if there are not enough frames saved from previous + // clip, the transition must be shortened accordingly + if (iProcessor->GetNumberOfSavedFrames() < iStartNumberOfTransitionFrames) + iStartNumberOfTransitionFrames = iProcessor->GetNumberOfSavedFrames(); + + iEndNumberOfTransitionFrames >>= 1; + if ( iNumberOfIncludedFrames < (iStartNumberOfTransitionFrames + iEndNumberOfTransitionFrames) ) + { + iStartNumberOfTransitionFrames = iNumberOfIncludedFrames >> 1; + iEndNumberOfTransitionFrames = iNumberOfIncludedFrames - iStartNumberOfTransitionFrames; + } + } + else + { + if ( iStartTransitionColor == EColorTransition ) + { + iStartNumberOfTransitionFrames >>= 1; + + // if there are not enough frames saved from previous + // clip, the transition must be shortened accordingly + if (iProcessor->GetNumberOfSavedFrames() < iStartNumberOfTransitionFrames) + iStartNumberOfTransitionFrames = iProcessor->GetNumberOfSavedFrames(); + } + + if ( iEndTransitionColor == EColorTransition ) + iEndNumberOfTransitionFrames >>= 1; + + if ( iNumberOfIncludedFrames < (iStartNumberOfTransitionFrames + iEndNumberOfTransitionFrames) ) + { + if ( iStartTransitionColor == EColorTransition ) + { + if ( ( iNumberOfIncludedFrames >> 1 ) > iStartNumberOfTransitionFrames ) + { + iEndNumberOfTransitionFrames = iNumberOfIncludedFrames - iStartNumberOfTransitionFrames; + } + else + { + iStartNumberOfTransitionFrames = iNumberOfIncludedFrames >> 1; + iEndNumberOfTransitionFrames = iNumberOfIncludedFrames - iStartNumberOfTransitionFrames; + } + } + else if ( iEndTransitionColor == EColorTransition ) + { + if ( ( iNumberOfIncludedFrames >> 1 ) > iEndNumberOfTransitionFrames ) + { + iStartNumberOfTransitionFrames = iNumberOfIncludedFrames - iEndNumberOfTransitionFrames; + } + else + { + iStartNumberOfTransitionFrames = iNumberOfIncludedFrames >> 1; + iEndNumberOfTransitionFrames = iNumberOfIncludedFrames - iStartNumberOfTransitionFrames; + } + } + else + { + iStartNumberOfTransitionFrames = iNumberOfIncludedFrames >> 1; + iEndNumberOfTransitionFrames = iNumberOfIncludedFrames - iStartNumberOfTransitionFrames; + } + } + } + } + // if transition is at one end only + else + { + if ( iStartOfClipTransition ) + { + iEndNumberOfTransitionFrames = 0; + if ( iStartTransitionColor == EColorTransition ) + { + iStartNumberOfTransitionFrames >>= 1; + + // if there are not enough frames saved from previous + // clip, the transition must be shortened accordingly + if (iProcessor->GetNumberOfSavedFrames() < iStartNumberOfTransitionFrames) + iStartNumberOfTransitionFrames = iProcessor->GetNumberOfSavedFrames(); + } + + if ( iNumberOfIncludedFrames < iStartNumberOfTransitionFrames ) + { + iStartNumberOfTransitionFrames = iNumberOfIncludedFrames; + } + } + else + { + iStartNumberOfTransitionFrames = 0; + if ( iEndTransitionColor == EColorTransition ) + { + iEndNumberOfTransitionFrames >>= 1; + } + if ( iNumberOfIncludedFrames < iEndNumberOfTransitionFrames ) + { + iEndNumberOfTransitionFrames = iNumberOfIncludedFrames; + } + } + } + // fetch the last Intra before transition begins. + // should be done after the cutting as well. + + iLastIntraFrameBeforeTransition=0; + if(iNumberOfIncludedFrames > 2) //so that we could loop to find the last intra. + { + TInt i; + TInt j=iProcessor->GetStartFrameIndex(); // processor needs frame index from beginning of clip + for(i=endFrameIndex-iEndNumberOfTransitionFrames; i>=startFrameIndex;i--) + { + if(iProcessor->GetVideoFrameType(i+j) == 1) // absolute index needed here! + { + iLastIntraFrameBeforeTransition=i; + break; + } + } + } + +} + +// --------------------------------------------------------- +// CVideoProcessor::SetTransitionFrameParams +// Set parameters for a transition frame +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::SetTransitionFrameParams(TInt aIncludedFrameNumber, TBool& aDecodeFrame) +{ + + // if transition is to be applied at the beginning of the clip + if(iStartOfClipTransition) + { + iFirstFrameAfterTransition = EFalse; + // this is for start-of-clip transition + if(aIncludedFrameNumber < iStartNumberOfTransitionFrames) + { + // if its first transition frame + if(aIncludedFrameNumber == 0) + { + iFirstTransitionFrame = 1; + iTransitionFrameNumber = 0; + } + else + { + iTransitionFrameNumber++; + } + + if ( iStartTransitionColor == EColorTransition ) + { + // ignore this transition if the previous clip has less transition frames + // than this clip's transition frames + if ( iTransitionFrameNumber < ( iProcessor->NumberOfTransition() - iTransitionFrameNumber ) ) + { + iTransitionFrame = 1; + iTransitionPosition = EPositionStartOfClip; + aDecodeFrame = ETrue; + iTransitionColor = iStartTransitionColor; + } + else + { + iPreviousFrameIncluded = EFalse; + } + } + else + { + iTransitionFrame = 1; + iTransitionPosition = EPositionStartOfClip; + aDecodeFrame = EFalse; + iTransitionColor = iStartTransitionColor; + } + + } + else + { + // if this is first frame after transition, we need to encode it as intra + // treat/simulate it as if its the start of the cut point. + if(aIncludedFrameNumber == iStartNumberOfTransitionFrames) + { + iFirstFrameAfterTransition = ETrue; + iPreviousFrameIncluded = EFalse; + } + } + } + + // if transition is to be applied at the end of the clip + if(iEndOfClipTransition && iTransitionFrame == 0) + { + // this is for end-of-clip transition + if(aIncludedFrameNumber >= iNumberOfIncludedFrames - iEndNumberOfTransitionFrames) + { + // if its first transition frame + if(aIncludedFrameNumber == iNumberOfIncludedFrames - iEndNumberOfTransitionFrames) + { + iFirstTransitionFrame = 1; + iTransitionFrameNumber = 0; + } + else + { + iTransitionFrameNumber++; + } + + if ( iEndTransitionColor == EColorTransition ) + { + // get the next clip's start transition information + GetNextClipTransitionInfo(); + // if next clip's start transition number is less than current clip's + // end transition number, then DO NOT treat current frame as the + // the transition frame + if ( ( iEndNumberOfTransitionFrames - iTransitionFrameNumber ) <= iNextTransitionNumber ) + { + iTransitionFrame = 1; + iTransitionPosition = EPositionEndOfClip; + aDecodeFrame = ETrue; + iTransitionColor = iEndTransitionColor; + } + } + else + { + iTransitionFrame = 1; + iTransitionPosition = EPositionEndOfClip; + aDecodeFrame = EFalse; + iTransitionColor = iEndTransitionColor; + } + } + else + { + // if this is first frame, we need to start decoding from here + // treat/simulate it as if its the nearest preceding intra frame. + if(iFrameNumber >= iLastIntraFrameBeforeTransition) + aDecodeFrame = ETrue; + + // In AVC case, if there is also starting transition, decode + // all frames after that since frame numbering must be consistent + // for AVC decoding to work + if (iDataFormat == EDataAVC && iStartOfClipTransition) + aDecodeFrame = ETrue; + + } // end-of-clip transition + } + +} + + + +// --------------------------------------------------------- +// CVideoProcessor::ApplyFadingTransitionEffect +// Applies fading transition effect for a YUV frame +// (other items were commented in a header). +// --------------------------------------------------------- +// + +void CVideoProcessor::ApplyFadingTransitionEffect(TUint8* aYUVPtr, + TTransitionPosition aTransitionPosition, + TTransitionColor aTransitionColor, + TInt aTransitionFramenumber) +{ + TInt i; + + TSize movieSize = iProcessor->GetMovieResolution(); + TInt yLength = movieSize.iWidth * movieSize.iHeight; + + TInt uLength = yLength>>2; + TUint8* yFrame = (TUint8*)aYUVPtr; + TUint8* uFrame = (TUint8*)(yFrame+yLength); + TUint8* vFrame = (TUint8*)(uFrame+uLength); + TInt position; + TChar chr; + TInt value; + TPtr8 ptr(0,0); + + // look-up tables to avoid floating point operations due to fractional weighting + // corresponding to 0.1, 26 values quantized to 8 + const TUint8 quantTable1[8] = { + 4, 7, 10, 13, 16, 19, 22, 24 }; + // corresponding to 0.2, 52 values quantized to 16 + const TUint8 quantTable2[16] = { + 4, 8, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 50 }; + // corresponding to 0.3, 77 values quantized to 16 + const TUint8 quantTable3[16] = { + 5, 10, 15, 20, 24, 29, 33, 38, 42, 47, 51, 56, 61, 66, 71, 75 }; + // corresponding to 0.4, 103 values quantized to 32 + const TUint8 quantTable4[32] = { + 5, 10, 14, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, + 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 101 }; + // corresponding to 0.5, 128 values quantized to 32 + const TUint8 quantTable5[32] = { + 3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59, 63, + 67, 71, 75, 79, 83, 87, 91, 95, 99, 103, 107, 111, 115, 119, 123, 126 }; + // corresponding to 0.6, 154 values quantized to 32 + const TUint8 quantTable6[32] = { + 5, 13, 20, 27, 32, 36, 41, 45, 50, 54, 59, 63, 68, 72, 77, 81, + 86, 90, 95, 99, 103, 108, 112, 117, 121, 126, 130, 135, 139, 144, 148, 152 }; + // corresponding to 0.7, 179 values quantized to 64 + const TUint8 quantTable7[64] = { + 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50, + 53, 56, 59, 62, 65, 68, 71, 74, 77, 80, 83, 86, 89, 92, 95, 98, + 101, 104, 107, 110, 113, 116, 119, 122, 125, 128, 131, 134, 137, 140, 143, 146, + 149, 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 178 }; + // corresponding to 0.8, 204 values quantized to 64 + const TUint8 quantTable8[64] = { + 5, 10, 15, 20, 25, 29, 33, 36, 40, 42, 45, 48, 51, 54, 57, 60, + 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, + 111, 114, 117, 120, 123, 126, 129, 132, 135, 138, 141, 144, 147, 150, 153, 156, + 159, 162, 165, 168, 171, 174, 177, 180, 183, 186, 189, 192, 195, 198, 201, 203 }; + // corresponding to 0.9, 230 values quantized to 64 + const TUint8 quantTable9[64] = { + 5, 10, 15, 19, 23, 26, 30, 33, 37, 40, 44, 47, 51, 54, 58, 61, + 65, 68, 72, 75, 79, 82, 86, 89, 93, 96, 100, 103, 107, 110, 114, 117, + 121, 124, 128, 131, 135, 138, 142, 145, 149, 152, 156, 159, 163, 166, 170, 173, + 177, 180, 184, 187, 191, 194, 198, 201, 205, 208, 212, 215, 219, 222, 226, 228 }; + const TUint8 indexTable[10]={1,2,3,4,5,6,7,8,9,10}; + + // figure out if the transition is at the beginning or end of the clip + TInt index; + switch(aTransitionPosition) + { + case EPositionStartOfClip: // start-of-clip transition + if( (index = iStartNumberOfTransitionFrames - aTransitionFramenumber-1) < 0 ) index = 0; + break; + case EPositionEndOfClip: // end-of-clip transition + if( (index = aTransitionFramenumber) >= iEndNumberOfTransitionFrames ) index = iEndNumberOfTransitionFrames - 1; + break; + default: + index = 0; + break; + } + position = indexTable[index]; + + if(aTransitionColor==EColorWhite) + { + switch(position) // white + { + case 10: // 0% frame1, 100% frame2 + // Y + value = 254; chr = value; + ptr.Set(yFrame, yLength, yLength); + ptr.Fill(chr); + // U,V + value = 128; chr = value; + ptr.Set(uFrame, uLength<<1, uLength<<1); + ptr.Fill(chr); + break; + case 9: // 10% frame1, 90% frame2 + value = quantTable9[63]; // 90% of 254 (white) + for(i=0; i>5] + value); + value = 113; // 90% of 128 + for(i=0; i>5] + value); + *vFrame = (TUint8)(quantTable1[(*vFrame)>>5] + value); + } + break; + case 8: // 20% frame1, 80% frame2 + value = quantTable8[63]; // 80% of 254 (white) + for(i=0; i>4] + value); + value = 98; // 80% of 128 + for(i=0; i>4] + value); + *vFrame = (TUint8)(quantTable2[(*vFrame)>>4] + value); + } + break; + case 7: // 30% frame1, 70% frame2 + value = quantTable7[63]; // 70% of 254 (white) + for(i=0; i>4] + value); + value = 86; // 70% of 128 + for(i=0; i>4] + value); + *vFrame = (TUint8)(quantTable3[(*vFrame)>>4] + value); + } + break; + case 6: // 40% frame1, 60% frame2 + value = quantTable6[31]; // 60% of 254 (white) + for(i=0; i>3] + value); + value = 72; //77; // 60% of 128 + for(i=0; i>3] + value); + *vFrame = (TUint8)(quantTable4[(*vFrame)>>3] + value); + } + break; + case 5: // 50% frame1, 50% frame2 + value = quantTable5[31]; // 50% of 254 (white) + for(i=0; i>3] + value); + value = 62; // 50% of 128 + for(i=0; i>3] + value); + *vFrame = (TUint8)(quantTable5[(*vFrame)>>3] + value); + } + break; + case 4: // 60% frame1, 40% frame2 + value = quantTable4[31]; // 40% of 254 (white) + for(i=0; i>3] + value); + value = 44; //51; // 40% of 128 + for(i=0; i>3] + value); + *vFrame = (TUint8)(quantTable6[(*vFrame)>>3] + value); + } + break; + case 3: // 70% frame1, 30% frame2 + value = quantTable3[15]; // 30% of 254 (white) + for(i=0; i>2] + value); + value = 28; //38; // 30% of 128 + for(i=0; i>2] + value); + *vFrame = (TUint8)(quantTable7[(*vFrame)>>2] + value); + } + break; + case 2: // 80% frame1, 20% frame2 + value = quantTable2[15]; // 20% of 254 (white) + for(i=0; i>2] + value); + value = 18; //25; // 20% of 128 + for(i=0; i>2] + value); + *vFrame = (TUint8)(quantTable8[(*vFrame)>>2] + value); + } + break; + case 1: // 90% frame1, 10% frame2 + value = quantTable1[7]; // 10% of 254 (white) + for(i=0; i>2] + value); + value = 8; //13; // 10% of 128 + for(i=0; i>2] + value); + *vFrame = (TUint8)(quantTable9[(*vFrame)>>2] + value); + } + break; + default: // e.g., 100% frame1, 0% frame2 + break; + } + } + else if(aTransitionColor==EColorBlack) // black + { + switch(position) + { + case 10: // 0% frame1, 100% frame2 + // Y + value = 4; chr = value; + ptr.Set(yFrame, yLength, yLength); + ptr.Fill(chr); + // U,V + value = 128; chr = value; + ptr.Set(uFrame, uLength<<1, uLength<<1); + ptr.Fill(chr); + break; + case 9: // 10% frame1, 90% frame2 + for(i=0; i>5]); + value = 113; // 90% of 128 + for(i=0; i>5] + value); + *vFrame = (TUint8)(quantTable1[(*vFrame)>>5] + value); + } + break; + case 8: // 20% frame1, 80% frame2 + for(i=0; i>4]); + value = 98; // 80% of 128 + for(i=0; i>4] + value); + *vFrame = (TUint8)(quantTable2[(*vFrame)>>4] + value); + } + break; + case 7: // 30% frame1, 70% frame2 + for(i=0; i>4]); + value = 86; // 70% of 128 + for(i=0; i>4] + value); + *vFrame = (TUint8)(quantTable3[(*vFrame)>>4] + value); + } + break; + case 6: // 40% frame1, 60% frame2 + for(i=0; i>3]); + value = 72; //77; // 60% of 128 + for(i=0; i>3] + value); + *vFrame = (TUint8)(quantTable4[(*vFrame)>>3] + value); + } + break; + case 5: // 50% frame1, 50% frame2 + for(i=0; i>3]); + value = 62; // 50% of 128 + for(i=0; i>3] + value); + *vFrame = (TUint8)(quantTable5[(*vFrame)>>3] + value); + } + break; + case 4: // 60% frame1, 40% frame2 + for(i=0; i>3]); + value = 44; //51; // 40% of 128 + for(i=0; i>3] + value); + *vFrame = (TUint8)(quantTable6[(*vFrame)>>3] + value); + } + break; + case 3: // 70% frame1, 30% frame2 + for(i=0; i>2]); + value = 28; //38; // 30% of 128 + for(i=0; i>2] + value); + *vFrame = (TUint8)(quantTable7[(*vFrame)>>2] + value); + } + break; + case 2: // 80% frame1, 20% frame2 + for(i=0; i>2]); + value = 18; //25; // 20% of 128 + for(i=0; i>2] + value); + *vFrame = (TUint8)(quantTable8[(*vFrame)>>2] + value); + } + break; + case 1: // 90% frame1, 10% frame2 + for(i=0; i>2]); + value = 8; //13; // 10% of 128 + for(i=0; i>2] + value); + *vFrame = (TUint8)(quantTable9[(*vFrame)>>2] + value); + } + break; + default: // e.g., 100% frame1, 0% frame2 + break; + } + } + return; +} + +// --------------------------------------------------------- +// CVideoProcessor::ApplyBlendingTransitionEffect +// Applies blending transition effect between two YUV frames +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::ApplyBlendingTransitionEffect(TUint8* aYUVPtr1, + TUint8* aYUVPtr2, + TInt aRepeatFrame, + TInt aTransitionFramenumber) +{ + TInt i; + TSize tempSize = iProcessor->GetMovieResolution(); + + TInt yLength = tempSize.iWidth*tempSize.iHeight; + TInt uLength = yLength>>2; + TInt yuvLength = yLength + (yLength>>1); + TUint8* yFrame1 = (TUint8*)aYUVPtr1; + TUint8* uFrame1 = (TUint8*)(yFrame1+yLength); + TUint8* vFrame1 = (TUint8*)(uFrame1+uLength); + TUint8* yFrame2 = (TUint8*)aYUVPtr2; + TUint8* uFrame2 = (TUint8*)(yFrame2+yLength); + TUint8* vFrame2 = (TUint8*)(uFrame2+uLength); + TInt position; + TPtr8 ptr(0,0); + const TInt numberOfTables = 10; + + // corresponding to 0.1, 26 values quantized to 8 + const TUint8 quantTable1[8] = { + 4, 7, 10, 13, 16, 19, 22, 24 }; + // corresponding to 0.2, 52 values quantized to 16 + const TUint8 quantTable2[16] = { + 4, 8, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 50 }; + // corresponding to 0.3, 77 values quantized to 16 + const TUint8 quantTable3[16] = { + 5, 10, 15, 20, 24, 29, 33, 38, 42, 47, 51, 56, 61, 66, 71, 75 }; + // corresponding to 0.4, 103 values quantized to 32 + const TUint8 quantTable4[32] = { + 5, 10, 14, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, + 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 101 }; + // corresponding to 0.5, 128 values quantized to 32 + const TUint8 quantTable5[32] = { + 3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59, 63, + 67, 71, 75, 79, 83, 87, 91, 95, 99, 103, 107, 111, 115, 119, 123, 126 }; + // corresponding to 0.6, 154 values quantized to 32 + const TUint8 quantTable6[32] = { + 5, 13, 20, 27, 32, 36, 41, 45, 50, 54, 59, 63, 68, 72, 77, 81, + 86, 90, 95, 99, 103, 108, 112, 117, 121, 126, 130, 135, 139, 144, 148, 152 }; + // corresponding to 0.7, 179 values quantized to 64 + const TUint8 quantTable7[64] = { + 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50, + 53, 56, 59, 62, 65, 68, 71, 74, 77, 80, 83, 86, 89, 92, 95, 98, + 101, 104, 107, 110, 113, 116, 119, 122, 125, 128, 131, 134, 137, 140, 143, 146, + 149, 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 178 }; + // corresponding to 0.8, 204 values quantized to 64 + const TUint8 quantTable8[64] = { + 5, 10, 15, 20, 25, 29, 33, 36, 40, 42, 45, 48, 51, 54, 57, 60, + 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, + 111, 114, 117, 120, 123, 126, 129, 132, 135, 138, 141, 144, 147, 150, 153, 156, + 159, 162, 165, 168, 171, 174, 177, 180, 183, 186, 189, 192, 195, 198, 201, 203 }; + // corresponding to 0.9, 230 values quantized to 64 + const TUint8 quantTable9[64] = { + 5, 10, 15, 19, 23, 26, 30, 33, 37, 40, 44, 47, 51, 54, 58, 61, + 65, 68, 72, 75, 79, 82, 86, 89, 93, 96, 100, 103, 107, 110, 114, 117, + 121, 124, 128, 131, 135, 138, 142, 145, 149, 152, 156, 159, 163, 166, 170, 173, + 177, 180, 184, 187, 191, 194, 198, 201, 205, 208, 212, 215, 219, 222, 226, 228 }; + + const TUint8 indexTable[10]={1,2,3,4,5,6,7,8,9,10}; + + // figure out the position of the index (determines which table to use) + TInt frameNumber = aTransitionFramenumber; + if(frameNumber>=iStartNumberOfTransitionFrames) frameNumber=iStartNumberOfTransitionFrames-1; + TInt index = (frameNumber<<1) + aRepeatFrame; + if(index>=numberOfTables) index=numberOfTables-1; + position = indexTable[index]; + + // calculate new values + switch(position) + { + case 10: // 0% frame1, 100% frame2 + ptr.Set(yFrame1,yuvLength,yuvLength); + ptr.Copy(yFrame2,yuvLength); + break; + case 9: // 10% frame1, 90% frame2 + for(i=0; i>5] + quantTable9[(*yFrame2)>>2]); + for(i=0; i>5] + quantTable9[(*uFrame2)>>2] - 10); + *vFrame1 = (TUint8)(quantTable1[(*vFrame1)>>5] + quantTable9[(*vFrame2)>>2] - 10); + } + break; + case 8: // 20% frame1, 80% frame2 + for(i=0; i>4] + quantTable8[(*yFrame2)>>2]); + for(i=0; i>4] + quantTable8[(*uFrame2)>>2] - 15); + *vFrame1 = (TUint8)(quantTable2[(*vFrame1)>>4] + quantTable8[(*vFrame2)>>2] - 15); + } + break; + case 7: // 30% frame1, 70% frame2 + for(i=0; i>4] + quantTable7[(*yFrame2)>>2]); + for(i=0; i>4] + quantTable7[(*uFrame2)>>2] - 15); + *vFrame1 = (TUint8)(quantTable3[(*vFrame1)>>4] + quantTable7[(*vFrame2)>>2] - 15); + } + break; + case 6: // 40% frame1, 60% frame2 + for(i=0; i>3] + quantTable6[(*yFrame2)>>3]); + for(i=0; i>3] + quantTable6[(*uFrame2)>>3] - 10); + *vFrame1 = (TUint8)(quantTable4[(*vFrame1)>>3] + quantTable6[(*vFrame2)>>3] - 10); + } + break; + case 5: // 50% frame1, 50% frame2 + for(i=0; i>3] + quantTable5[(*yFrame2)>>3]); + for(i=0; i>3] + quantTable5[(*uFrame2)>>3] - 5); + *vFrame1 = (TUint8)(quantTable5[(*vFrame1)>>3] + quantTable5[(*vFrame2)>>3] - 5); + } + break; + case 4: // 60% frame1, 40% frame2 + for(i=0; i>3] + quantTable4[(*yFrame2)>>3]); + for(i=0; i>3] + quantTable4[(*uFrame2)>>3] - 10); + *vFrame1 = (TUint8)(quantTable6[(*vFrame1)>>3] + quantTable4[(*vFrame2)>>3] - 10); + } + break; + case 3: // 70% frame1, 30% frame2 + for(i=0; i>2] + quantTable3[(*yFrame2)>>4]); + for(i=0; i>2] + quantTable3[(*uFrame2)>>4] - 8); + *vFrame1 = (TUint8)(quantTable7[(*vFrame1)>>2] + quantTable3[(*vFrame2)>>4] - 8); + } + break; + case 2: // 80% frame1, 20% frame2 + for(i=0; i>2] + quantTable2[(*yFrame2)>>4]); + for(i=0; i>2] + quantTable2[(*uFrame2)>>4] - 8); + *vFrame1 = (TUint8)(quantTable8[(*vFrame1)>>2] + quantTable2[(*vFrame2)>>4] - 8); + } + break; + case 1: // 90% frame1, 10% frame2 + for(i=0; i>2] + quantTable1[(*yFrame2)>>5]); + for(i=0; i>2] + quantTable1[(*uFrame2)>>5] - 5); + *vFrame1 = (TUint8)(quantTable9[(*vFrame1)>>2] + quantTable1[(*vFrame2)>>5] - 5); + } + break; + default: // e.g., 100% frame1, 0% frame2 + break; + } + return; +} + + +// --------------------------------------------------------- +// CVideoProcessor::ApplySlidingTransitionEffect +// Applies sliding transition effect between two YUV frames +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::ApplySlidingTransitionEffect(TUint8* aYUVPtr1, + TUint8* aYUVPtr2, + TVedMiddleTransitionEffect aVedMiddleTransitionEffect, + TInt aRepeatFrame, + TInt aTransitionFramenumber) +{ + TInt i; + TSize tempSize = iProcessor->GetMovieResolution(); + TInt yLength = tempSize.iWidth*tempSize.iHeight; + TInt uLength = yLength>>2; + TInt yWidth = tempSize.iWidth; + TInt uWidth = tempSize.iWidth>>1; + TInt yHeight = tempSize.iHeight; + TInt uHeight = tempSize.iHeight>>1; + TUint8* yFrame1 = (TUint8*)aYUVPtr1; + TUint8* uFrame1 = (TUint8*)(yFrame1+yLength); + TUint8* vFrame1 = (TUint8*)(uFrame1+uLength); + TUint8* yFrame2 = (TUint8*)aYUVPtr2; + TUint8* uFrame2 = (TUint8*)(yFrame2+yLength); + TUint8* vFrame2 = (TUint8*)(uFrame2+uLength); + TPtr8 ptr(0,0); + TInt offset = 0; + TInt ySliceWidth = 0; + TInt uSliceWidth = 0; + TInt sliceSize = 0; + TInt frameNumber = (aTransitionFramenumber<<1) + aRepeatFrame; + + switch(aVedMiddleTransitionEffect) + { + case EVedMiddleTransitionEffectWipeLeftToRight: + // figure out the amount of data to change + VDASSERT(iStartNumberOfTransitionFrames,19); + ySliceWidth = (TInt)((TReal)yWidth * (TReal)(frameNumber+1)/(TReal)(iStartNumberOfTransitionFrames<<1) + 0.5); + if(ySliceWidth>yWidth) ySliceWidth = yWidth; + uSliceWidth = ySliceWidth>>1; + // copy the relevant portions of the image from frame2 to frame1 + // y + for(i=0; iyWidth) ySliceWidth = yWidth; + uSliceWidth = ySliceWidth>>1; + // evaluate the yuv offsets and new positions to point to in the buffer + offset = yWidth-ySliceWidth; + yFrame1+=offset; + yFrame2+=offset; + offset = uWidth-uSliceWidth; + uFrame1+=offset; + uFrame2+=offset; + vFrame1+=offset; + vFrame2+=offset; + // copy the relevant portions of the image from frame2 to frame1 + // y + for(i=0; iyHeight) ySliceWidth = yHeight; + uSliceWidth = ySliceWidth>>1; + // copy the relevant portions of the image from frame2 to frame1 + // y + sliceSize = ySliceWidth * yWidth; + ptr.Set(yFrame1,sliceSize,sliceSize); + ptr.Copy(yFrame2,sliceSize); + // u,v + sliceSize = uSliceWidth * uWidth; + ptr.Set(uFrame1,sliceSize,sliceSize); + ptr.Copy(uFrame2,sliceSize); + ptr.Set(vFrame1,sliceSize,sliceSize); + ptr.Copy(vFrame2,sliceSize); + break; + case EVedMiddleTransitionEffectWipeBottomToTop: + // figure out the amount of data to change + VDASSERT(iStartNumberOfTransitionFrames,22); + ySliceWidth = (TInt)((TReal)yHeight * (TReal)(frameNumber+1)/(TReal)(iStartNumberOfTransitionFrames<<1) + 0.5); + if(ySliceWidth>yHeight) ySliceWidth = yHeight; + uSliceWidth = ySliceWidth>>1; + // evaluate the yuv offsets and new positions to point to in the buffer + offset = (yHeight-ySliceWidth) * yWidth; + yFrame1+=offset; + yFrame2+=offset; + offset = (uHeight-uSliceWidth) * uWidth; + uFrame1+=offset; + uFrame2+=offset; + vFrame1+=offset; + vFrame2+=offset; + // copy the relevant portions of the image from frame2 to frame1 + // y + sliceSize = ySliceWidth * yWidth; + ptr.Set(yFrame1,sliceSize,sliceSize); + ptr.Copy(yFrame2,sliceSize); + // u,v + sliceSize = uSliceWidth * uWidth; + ptr.Set(uFrame1,sliceSize,sliceSize); + ptr.Copy(uFrame2,sliceSize); + ptr.Set(vFrame1,sliceSize,sliceSize); + ptr.Copy(vFrame2,sliceSize); + break; + case EVedMiddleTransitionEffectNone: + case EVedMiddleTransitionEffectDipToBlack: + case EVedMiddleTransitionEffectDipToWhite: + case EVedMiddleTransitionEffectCrossfade: + case EVedMiddleTransitionEffectLast: + default: + break; + } + return; +} + + +// --------------------------------------------------------- +// CVideoProcessor::ApplySpecialEffect +// Applies color effect for a YUV frame +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::ApplySpecialEffect(TInt aColorEffect, TUint8* aYUVDataPtr, + TInt aColorToneU, TInt aColorToneV) +{ + VDASSERT(aYUVDataPtr,23); + VDASSERT(iVideoWidth,24); + VDASSERT(iVideoHeight,25); + TChar chr; + TInt value; + TInt offset; + TInt length; + // Values for the U & V Fill parameters + TInt uFillValue, vFillValue; + TPtr8 ptr(0,0); + TSize tempSize = iProcessor->GetMovieResolution(); + + // asad - check if mpeg4, then change pixel range from (-128,127) to (0,255) + if (iProcessor->GetOutputVideoType() == EVedVideoTypeMPEG4SimpleProfile) + { + // U + aColorToneU += 128; + if (aColorToneU<0) aColorToneU = 0; + if (aColorToneU>255) aColorToneU = 255; + // V + aColorToneV += 128; + if (aColorToneV<0) aColorToneV = 0; + if (aColorToneV>255) aColorToneV = 255; + } + TChar uChr, vChr; + switch(aColorEffect) + { + case 0/*None*/: + return; + case 1/*BW*/: + value = 128; + chr = value; + offset = tempSize.iWidth*tempSize.iHeight; + length = offset>>1; // u,v data length (2*L/2*W/2) + ptr.Set((TUint8*)(aYUVDataPtr+offset), length, length); + ptr.Fill((TChar)chr); + break; + case 2: + offset = tempSize.iWidth*tempSize.iHeight; + length = offset>>2; + uFillValue = aColorToneU; + uChr = uFillValue; + vFillValue = aColorToneV; + vChr = vFillValue; + + ptr.Set((TUint8*)(aYUVDataPtr + offset), length, length); + ptr.Fill((TChar)uChr); + + offset = 1.25 * offset; // For filling the v-value + ptr.Set((TUint8*)(aYUVDataPtr + offset), length, length); + ptr.Fill((TChar)vChr); + break; + default: + return; + } +} + +// --------------------------------------------------------- +// CVideoProcessor::TFrameOperation2TInt +// Converts frame operation enumeration to int +// (other items were commented in a header). +// --------------------------------------------------------- +// +TInt CVideoProcessor::TFrameOperation2TInt(TDecoderFrameOperation aFrameOperation) +{ + switch(aFrameOperation) + { + case EDecodeAndWrite: + return 1; + case EDecodeNoWrite: + return 2; + case EWriteNoDecode: + return 3; + case ENoDecodeNoWrite: + return 4; + default: + return KErrGeneral; + } +} + +// --------------------------------------------------------- +// CVideoProcessor::TColorEffect2TInt +// Converts color effect enumeration to int +// (other items were commented in a header). +// --------------------------------------------------------- +// +TInt CVideoProcessor::TColorEffect2TInt(TVedColorEffect aColorEffect) +{ + switch(aColorEffect) + { + case EVedColorEffectNone: + return 0; + case EVedColorEffectBlackAndWhite: + return 1; + case EVedColorEffectToning: + return 2; + default: + return KErrGeneral; + } +} + + + +// --------------------------------------------------------- +// CVideoProcessor::InputDataAvailable +// Overridden CDataProcessor::InputDataAvailable() method +// Called when new input blocks are available +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::InputDataAvailable(TAny* /*aUserPointer*/) +{ + PRINT((_L("CVideoProcessor::InputDataAvailable()"))); + // Signal ourselves if we are decoding and a request is not + // pending: + if ( iDecoding && !iTranscoderInitPending && !iDecodePending && + (iStatus == KRequestPending) ) + { + PRINT((_L("CVideoProcessor::InputDataAvailable() - complete request"))); + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + } +} + + +// --------------------------------------------------------- +// CVideoProcessor::StreamEndReached +// Overridden CDataProcessor::StreamEndReached() method +// Called when input stream has ended +// (other items were commented in a header). +// --------------------------------------------------------- +// + +void CVideoProcessor::StreamEndReached(TAny* /*aUserPointer*/) +{ + PRINT((_L("CVideoProcessor::StreamEndReached()"))); + iStreamEnd = ETrue; +} + +// --------------------------------------------------------- +// CVideoProcessor::DoCancel +// Cancels any asynchronous requests pending. +// +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::DoCancel() +{ + + // Cancel our internal request + if ( iStatus == KRequestPending ) + { + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrCancel); + } + +} + +// --------------------------------------------------------- +// CVideoProcessor::GetNextClipTransitionInfo +// Get the start transition info of the next clip. +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CVideoProcessor::GetNextClipTransitionInfo() +{ + if ( iNextTransitionNumber == -1 ) + { + iNextTransitionNumber = iProcessor->NextClipStartTransitionNumber(); + } +} + +// --------------------------------------------------------- +// CVideoProcessor::DetermineResolutionChange +// This function checks if the video clip is needed to be resampled +// (other items were commented in a header). +// --------------------------------------------------------- +// Resolution Transcoder, check if this video clip need to be resample + +TBool CVideoProcessor::DetermineResolutionChange() +{ + TSize VideoClipResolution = iProcessor->GetVideoClipResolution(); + TSize MovieResolution = iProcessor->GetMovieResolution(); + if (VideoClipResolution != MovieResolution) + return ETrue; + else + return EFalse; +} + +// --------------------------------------------------------- +// CVideoProcessor::DetermineFrameRateChange +// This function checks if the frame rate must be changed +// => clip re-encoded +// (other items were commented in a header). +// --------------------------------------------------------- +// +TBool CVideoProcessor::DetermineFrameRateChange() + { + + TReal clipFrameRate = iProcessor->GetVideoClipFrameRate(); + TReal movieFrameRate = iProcessor->GetMovieFrameRate(); + + // Do re-encoding only when reducing frame rate, + // otherwise we would have to come up with new frames + if ( movieFrameRate > 0 && clipFrameRate > movieFrameRate ) + return ETrue; + + return EFalse; + + } + +// --------------------------------------------------------- +// CVideoProcessor::DetermineFrameRateChange +// This function checks if the bitrate must be changed +// => clip re-encoded +// (other items were commented in a header). +// --------------------------------------------------------- +// +TBool CVideoProcessor::DetermineBitRateChange() + { + + // : AVC bitrate transcoding from a higher level + // to lower if resolution transcoding is not needed + + if ( iProcessor->GetMovieVideoBitrate() > 0 ) // movie has bitrate restriction => need to transcode + return ETrue; + + if ( iProcessor->GetOutputVideoType() == EVedVideoTypeH263Profile0Level10 ) + { + if (iDataFormat == EDataH263) + { + if ( (iProcessor->GetCurrentClipVideoType() != EVedVideoTypeH263Profile0Level10) && + (iProcessor->GetCurrentClipVideoType() != EVedVideoTypeUnrecognized) ) + return ETrue; + } + + else if (iDataFormat == EDataMPEG4) + { + // level 0 and level 1 max bitrate is 64 kb/s + // others need to be transcoded + if (iInputMPEG4ProfileLevelId != 8 && iInputMPEG4ProfileLevelId != 1) + return ETrue; + } + } + + return EFalse; + + } + + +// --------------------------------------------------------- +// CVideoProcessor::GetFrameDuration +// Calculates the duration of current frame +// (other items were commented in a header). +// --------------------------------------------------------- +// +TInt CVideoProcessor::GetFrameDuration(TInt aFrameNumber) +{ + // calculate frame duration in ticks + TInt startFrame = iProcessor->GetOutputNumberOfFrames() - iNumberOfFrames; + TInt absFrameNumber = startFrame + aFrameNumber; + TInt cur = absFrameNumber; + TInt next = cur+1; + + // frameDuration is in ticks, with timescale of the current input clip + if(next >= iProcessor->GetOutputNumberOfFrames()) + { + return I64INT(iProcessor->GetVideoClipDuration() - iProcessor->VideoFrameTimeStamp(cur) ); + } + else + { + return I64INT( iProcessor->VideoFrameTimeStamp(next) - iProcessor->VideoFrameTimeStamp(cur) ); + } +} + +// --------------------------------------------------------- +// CVideoProcessor::CheckVosHeaderL +// Checks whether the resynch bit is set if set then resets to zero +// (other items were commented in a header). +// @return TBool +// --------------------------------------------------------- +// + +TBool CVideoProcessor::CheckVosHeaderL(TPtrC8& aBuf) +{ + return iDecoder->CheckVOSHeaderL((TPtrC8&)aBuf); +} + + +// --------------------------------------------------------- +// CVideoProcessor::RenderFrame +// The H.263 decoder calls this function when a decoded +// frame is available for retrieving +// @return TInt +// --------------------------------------------------------- +// +TInt CVideoProcessor::RenderFrame(TAny* aFrame) +{ + iDecoder->FrameRendered(aFrame); + return KErrNone; +} + +// --------------------------------------------------------- +// CVideoProcessor::MtoTimerElapsed +// Called when timer has elapsed +// --------------------------------------------------------- +// +void CVideoProcessor::MtoTimerElapsed(TInt aError) +{ + + PRINT((_L("CVideoProcessor::MtoTimerElapsed() begin"))); + + if (aError != KErrNone) + { + iMonitor->Error(aError); + return; + } + + VDASSERT(iFrameInfoArray[0].iEncodeFrame == 1, 110); + + // if next frame in encode queue is an intermediate modification frame + // and modification has not yet been applied, start waiting timer again + if ( ( (iFrameInfoArray[0].iTransitionFrame == 1) || + (iProcessor->GetColorEffect() != EVedColorEffectNone) ) && + iFrameInfoArray[0].iModificationApplied == 0 ) + { + PRINT((_L("CVideoProcessor::MtoTimerElapsed() - modification not done yet, set timer"))); + + iTimer->SetTimer( TTimeIntervalMicroSeconds32( GetEncodingDelay() ) ); + return; + } + + PRINT((_L("CVideoProcessor::MtoTimerElapsed() - removing pic with ts %d ms"), + I64INT(iProcessor->GetVideoTimeInMsFromTicks(iFrameInfoArray[0].iTimeStamp, EFalse)) )) + + // save frame number to be able to recover in case the frame + // gets encoded regardless of the delay + iSkippedFrameNumber = iFrameInfoArray[0].iFrameNumber; + + // remove skipped frame from queue + iFrameInfoArray.Remove(0); + + PRINT((_L("CVideoProcessor::MtoTimerElapsed() - %d items in queue"), iFrameInfoArray.Count())); + + if (iDecodingSuspended && !iStreamEndRead) + { + if (iFrameInfoArray.Count() < iMaxItemsInProcessingQueue && !iDelayedWrite) + { + PRINT((_L("CVideoProcessor::MtoTimerElapsed() - Resume decoding"))); + iDecodingSuspended = EFalse; + // activate object to start decoding + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + return; + } + } + + if ( !IsEncodeQueueEmpty() ) + { + // if there are still frames to be encoded, and next one is waiting, + // set timer again + if ( IsNextFrameBeingEncoded() ) + { + if ( !iTimer->IsPending() ) + { + PRINT((_L("CVideoProcessor::MtoTimerElapsed(), set timer again"))); + iTimer->SetTimer( TTimeIntervalMicroSeconds32( iMaxEncodingDelay ) ); + } + } + return; + } + + if (iDelayedWrite) + { + PRINT((_L("CVideoProcessor::MtoTimerElapsed(), writing delayed frame"))); + // write delayed frame + TRAPD( error, WriteDelayedFrameL() ); + if (error != KErrNone) + { + iMonitor->Error(aError); + return; + } + } + + if ( iStreamEndRead ) + { + if ( iFrameInfoArray.Count() != 0 ) + { + // return now if we have read stream end, but there are still frames waiting + // to be decoded => processing will be completed in MtroPictureFromTranscoder + PRINT((_L("CVideoProcessor::MtoTimerElapsed(), end read but frames in progress, return"))); + return; + } + + else + { + // activate to stop processing + PRINT((_L("CVideoProcessor::MtoTimerElapsed(), end read and all frames processed, stopping"))); + iProcessingComplete = ETrue; + } + } + + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + // activate object to continue/end processing + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + PRINT((_L("CVideoProcessor::MtoTimerElapsed() end"))); +} + +// --------------------------------------------------------- +// CVideoProcessor::WriteBufferL +// Called by transcoder to pass an encoded buffer +// --------------------------------------------------------- +// +void CVideoProcessor::WriteBufferL(CCMRMediaBuffer* aBuffer) +{ + if ( iFrameInfoArray.Count() == 0 ) + { + PRINT((_L("CVideoProcessor::WriteBufferL() not ready to receive buffer, return"))); + return; + } + + if ( ((iFrameInfoArray[0].iTranscoderMode != EFullWithIM) && + (iFrameInfoArray[0].iTranscoderMode != EFull)) || + (iFrameInfoArray[0].iEncodeFrame != 1) ) + { + PRINT((_L("CVideoProcessor::WriteBufferL() encoded picture received but not expected, ignore & return"))); + return; + } + + + // cancel timer + iTimer->CancelTimer(); + + if (aBuffer->Type() == CCMRMediaBuffer::EVideoMPEG4DecSpecInfo) + { + // should not happen + return; + } + + TTimeIntervalMicroSeconds encodedTs = aBuffer->TimeStamp(); + + PRINT((_L("CVideoProcessor::WriteBufferL(), timestamp %d ms, keyFrame = %d"), + I64INT( encodedTs.Int64() ) / 1000, aBuffer->RandomAccessPoint() )); + + TBool removeItem = ETrue; + TInt64 timeStamp = 0; + TInt frameNumber = 0; + + while (iFrameInfoArray.Count()) + { + TTimeIntervalMicroSeconds ts = (iProcessor->GetVideoTimeInMsFromTicks(iFrameInfoArray[0].iTimeStamp, EFalse)) * 1000; + + if (ts < encodedTs) + { + // a frame has been skipped + iFrameInfoArray.Remove(0); + PRINT((_L("CVideoProcessor::WriteBufferL() frame skipped - number of items in queue: %d"), iFrameInfoArray.Count())); + } + else if (ts > encodedTs) + { + // this frame has most likely been treated as skipped using timer, + // but it was encoded regardless + removeItem = EFalse; + frameNumber = iSkippedFrameNumber; + timeStamp = iProcessor->GetVideoTimeInTicksFromMs( encodedTs.Int64() / 1000, EFalse ); + + PRINT((_L("CVideoProcessor::WriteBufferL() frame skipped falsely, ts in ticks = %d"), I64INT(timeStamp))); + break; + } + else + { + // normal case, encoded frame timestamp is as expected + timeStamp = iFrameInfoArray[0].iTimeStamp; + frameNumber = iFrameInfoArray[0].iFrameNumber; + break; + } + } + + VDASSERT(iFrameInfoArray.Count(), 50); + + // set descriptor for writing the frame + TPtr8 writeDes(0,0); + writeDes.Set(const_cast(aBuffer->Data().Ptr()), + aBuffer->Data().Length(), aBuffer->Data().Length()); + + HBufC8* tempBuffer = 0; + TInt error; + + if ( (iProcessor->GetOutputVideoType() == EVedVideoTypeMPEG4SimpleProfile) && + (!iOutputVolHeaderWritten) && (frameNumber >= iFirstIncludedFrameNumber) ) + { + VDASSERT(iOutputVolHeader, 51); + + // MPEG-4 output: + // Encoded frame is the first one of the clip, insert VOL header at the beginning + // Allocate a temp buffer to include vol header and the encoded frame + TInt length = iOutputVolHeader->Length() + aBuffer->Data().Length(); + TRAP(error, tempBuffer = (HBufC8*) HBufC8::NewL(length) ); + + if (error != KErrNone) + { + iMonitor->Error(error); + return; + } + + TPtr8 ptr( tempBuffer->Des() ); + ptr.Copy(iOutputVolHeader->Des()); + ptr.Append(aBuffer->Data()); + + writeDes.Set(tempBuffer->Des()); + iOutputVolHeaderWritten = ETrue; + } + +#ifdef VIDEOEDITORENGINE_AVC_EDITING + if (iProcessor->GetOutputVideoType() == EVedVideoTypeAVCBaselineProfile) + { + + // get number of NAL units + TUint8* tmp = const_cast(aBuffer->Data().Ptr() + aBuffer->Data().Length()); + + tmp -= 4; + TInt numNalUnits = TInt(tmp[0]) + (TInt(tmp[1])<<8) + (TInt(tmp[2])<<16) + (TInt(tmp[3])<<24); + + TInt totalLength = 0; + + // get nal_unit_type of first NAL + TUint8* type = const_cast(aBuffer->Data().Ptr()); + TInt nalType = *type & 0x1F; + + PRINT((_L("CVideoProcessor::WriteBufferL() - # of NAL units = %d, frame # = %d, nal_unit_type = %d"), + numNalUnits, frameNumber, nalType)); + + if (nalType != 1 && nalType != 5) + { + // there are extra SPS/PPS units in the beginning + // of the buffer, skip those + numNalUnits--; + + if (numNalUnits == 0) + { + PRINT((_L("CVideoProcessor::WriteBufferL() No NAL units left, return"))); + return; + } + + // point to first length field + tmp = const_cast(aBuffer->Data().Ptr() + aBuffer->Data().Length()); + tmp -= 4; // # + tmp -= numNalUnits * 8; // offset & length for each NAL + tmp += 4; // skip offset + + // get NAL length + TInt len = TInt(tmp[0]) + (TInt(tmp[1])<<8) + (TInt(tmp[2])<<16) + (TInt(tmp[3])<<24); + type += len; + nalType = *type & 0x1F; + + while (nalType != 1 && nalType != 5 && numNalUnits) + { + numNalUnits--; + tmp += 8; + len = TInt(tmp[0]) + (TInt(tmp[1])<<8) + (TInt(tmp[2])<<16) + (TInt(tmp[3])<<24); + type += len; + nalType = *type & 0x1F; + } + tmp = const_cast(aBuffer->Data().Ptr() + aBuffer->Data().Length()) - 4; + } + + if (numNalUnits == 0) + { + PRINT((_L("CVideoProcessor::WriteBufferL() No NAL units left, return"))); + return; + } + + // rewind to last length field + tmp -= 4; + + // get total length of slices + for (TInt x = numNalUnits; x > 0; x--) + { + totalLength += TInt(tmp[0]) + (TInt(tmp[1])<<8) + (TInt(tmp[2])<<16) + (TInt(tmp[3])<<24); + tmp -= 8; + } + + TInt tempLength = totalLength + numNalUnits*4; + + // allocate output buffer + TRAP(error, tempBuffer = (HBufC8*) HBufC8::NewL(tempLength) ); + if (error != KErrNone) + { + iMonitor->Error(error); + return; + } + + TUint8* dst = const_cast(tempBuffer->Des().Ptr()); + TUint8* src = const_cast(aBuffer->Data().Ptr()); + + // point to first offset field + tmp += 4; + + for (TInt x = numNalUnits; x > 0; x--) + { + // get length + tmp += 4; + TInt length = TInt(tmp[0]) + (TInt(tmp[1])<<8) + (TInt(tmp[2])<<16) + (TInt(tmp[3])<<24); + + // set length + dst[0] = TUint8((length >> 24) & 0xff); + dst[1] = TUint8((length >> 16) & 0xff); + dst[2] = TUint8((length >> 8) & 0xff); + dst[3] = TUint8(length & 0xff); + dst += 4; + + // copy data + TPtr8 ptr(dst, length); + ptr.Copy(src, length); + + dst += length; + src += length; + + // point to next offset field + tmp +=4; + } + + writeDes.Set(tempBuffer->Des()); + writeDes.SetLength(tempLength); + + } +#endif + + // Figure out are we writing frames from the first + // or second clip in color transition + + TBool colorTransitionFlag = ETrue; + TInt index = KNumTransitionFrames / 4; + + if ( iFrameInfoArray[0].iTransitionFrame == 1 && + iFrameInfoArray[0].iTransitionPosition == EPositionStartOfClip && + iStartTransitionColor == EColorTransition ) + { + if ( ( (iFrameInfoArray[0].iTransitionFrameNumber == index) && + (iFrameInfoArray[0].iRepeatFrame == 0) ) || + iFrameInfoArray[0].iTransitionFrameNumber < index ) + { + colorTransitionFlag = EFalse; + } + } + + // write frame + error = iProcessor->WriteVideoFrameToFile((TDesC8&)writeDes, + timeStamp, 0 /*dummy*/, + aBuffer->RandomAccessPoint(), EFalse, colorTransitionFlag, ETrue ); + + if (tempBuffer) + delete tempBuffer; + + if (error == KErrCompletion) + { + PRINT((_L("CVideoProcessor::WriteBufferL() - processing complete"))); + // stop processing + iProcessingComplete = ETrue; + iFrameInfoArray.Reset(); + VDASSERT(iTranscoderStarted, 51); + iTransCoder->StopL(); + iTranscoderStarted = EFalse; + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + + // activate object to end processing + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + return; + } + + else if (error != KErrNone) + { + iMonitor->Error(error); + return; + } + + TInt startFrame = iProcessor->GetOutputNumberOfFrames() - iNumberOfFrames; + TInt absFrameNumber = startFrame + frameNumber; + + // save frame number + iLastWrittenFrameNumber = frameNumber; + + iProcessor->SetFrameType(absFrameNumber, aBuffer->RandomAccessPoint()); + + if (removeItem) + { + iFrameInfoArray.Remove(0); + + PRINT((_L("CVideoProcessor::WriteBufferL() - removed encoded pic, %d items in queue"), iFrameInfoArray.Count())); + } + else + PRINT((_L("CVideoProcessor::WriteBufferL() - did not remove encoded pic, %d items in queue"), iFrameInfoArray.Count())); + + + if (iDecodingSuspended && !iStreamEndRead) + { + if (iFrameInfoArray.Count() < iMaxItemsInProcessingQueue && !iDelayedWrite) + { + PRINT((_L("CVideoProcessor::WriteBufferL() - Resume decoding"))); + iDecodingSuspended = EFalse; + // activate object to start decoding + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + return; + } + } + + // if there are still frames to be encoded, start timer again + if ( !IsEncodeQueueEmpty() ) + { + // check if the next frame in queue is waiting to be encoded, set timer if so + if ( IsNextFrameBeingEncoded() ) + { + if ( !iTimer->IsPending() ) + { + PRINT((_L("CVideoProcessor::WriteBufferL(), set timer"))); + iTimer->SetTimer( TTimeIntervalMicroSeconds32( iMaxEncodingDelay ) ); + } + } + return; + } + + if (iStreamEndRead && !iDelayedWrite) + { + + PRINT((_L("CVideoProcessor::WriteBufferL() - stream end read & !iDelayedWrite"))); + + // stream end has been read + if (iFrameInfoArray.Count() == 0) + { + PRINT((_L("CVideoProcessor::WriteBufferL() - stream end read, no frames left"))); + // end + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + iProcessingComplete = ETrue; + // activate object to end processing + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + } + // else there are frames to be decoded, processing will be completed + // MtroPictureFromTranscoder + return; + } + + if (iDelayedWrite) + { + if ( IsEncodeQueueEmpty() ) + { + PRINT((_L("CVideoProcessor::WriteBufferL() writing delayed frame"))); + + TRAP(error, WriteDelayedFrameL()); + if (error != KErrNone) + { + iMonitor->Error(error); + return; + } + + if ( iStreamEndRead ) + { + if ( iFrameInfoArray.Count() != 0 ) + { + // return now if we have read stream end, but there are still frames waiting + // to be decoded => processing will be completed in MtroPictureFromTranscoder + PRINT((_L("CVideoProcessor::WriteBufferL(), end read but frames in progress, return"))); + return; + } + else + { + // activate to stop processing + PRINT((_L("CVideoProcessor::WriteBufferL(), end read and all frames processed, stopping"))); + iProcessingComplete = ETrue; + } + } + + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + // activate object to continue/end processing + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + return; + } + else + { + // check if the next frame in queue is waiting to be encoded, set timer if so + if ( IsNextFrameBeingEncoded() ) + { + if ( !iTimer->IsPending() ) + { + PRINT((_L("CVideoProcessor::WriteBufferL() - iDelayedWrite, set timer"))); + iTimer->SetTimer( TTimeIntervalMicroSeconds32( iMaxEncodingDelay ) ); + } + return; + } + } + } + +} + +// --------------------------------------------------------- +// CVideoProcessor::IsEncodeQueueEmpty +// (other items were commented in a header) +// --------------------------------------------------------- +// +TBool CVideoProcessor::IsEncodeQueueEmpty() +{ + + // check if there are still frames waiting to be encoded + for (TInt i = 0; i < iFrameInfoArray.Count(); i++) + { + if (iFrameInfoArray[i].iEncodeFrame == 1) + { + return EFalse; + } + } + return ETrue; + +} + +// --------------------------------------------------------- +// CVideoProcessor::IsNextFrameBeingEncoded +// (other items were commented in a header) +// --------------------------------------------------------- +// +TBool CVideoProcessor::IsNextFrameBeingEncoded() +{ + + // check if the next frame in queue is waiting to be encoded + if ( iFrameInfoArray.Count() && (iFrameInfoArray[0].iEncodeFrame == 1) ) + { + + VDASSERT( ( iFrameInfoArray[0].iTranscoderMode == EFull || + iFrameInfoArray[0].iTranscoderMode == EFullWithIM ), 120 ); + + if ( (iFrameInfoArray[0].iTranscoderMode == EFull) || + (iFrameInfoArray[0].iModificationApplied == 1) ) + { + return ETrue; + } + } + + return EFalse; + +} + +// --------------------------------------------------------- +// CVideoProcessor::GetEncodingDelay +// (other items were commented in a header) +// --------------------------------------------------------- +// +TInt CVideoProcessor::GetEncodingDelay() +{ + + // number of decode only -frames in queue before first encode frame + TInt numDecodeFrames = 0; + + TInt i; + for (i = 0; i < iFrameInfoArray.Count(); i++) + { + // get index of next encode frame in queue + if (iFrameInfoArray[i].iEncodeFrame == 1) + break; + else + numDecodeFrames++; + } + + VDASSERT(i < iFrameInfoArray.Count(), 112); + + TInt delay = iMaxEncodingDelay; + + // If the next frame in encoding queue is an intermediate modification frame + // (either transition frame or color effect has to be applied) + // and modification has not been applied to it, double the default delay + if ( ( (iFrameInfoArray[0].iTransitionFrame == 1) || + (iProcessor->GetColorEffect() != EVedColorEffectNone) ) && + iFrameInfoArray[0].iModificationApplied == 0 ) + { + PRINT((_L("CVideoProcessor::GetEncodingDelay() - double the delay"))); + delay <<= 1; + } + + // add time to process decode-only frames to delay + delay += numDecodeFrames * (iMaxEncodingDelay / 2); + + PRINT((_L("CVideoProcessor::GetEncodingDelay() - encoding delay = %d ms, num decode frames %d"), delay/1000, numDecodeFrames)); + + return delay; + + + +} + +// --------------------------------------------------------- +// CVideoProcessor::WriteDelayedFrameL +// (other items were commented in a header) +// --------------------------------------------------------- +// +void CVideoProcessor::WriteDelayedFrameL() +{ + + PRINT((_L("CVideoProcessor::WriteDelayedFrameL() begin"))); + + // write the delayed frame + TPtr8 ptr(iDelayedBuffer->Des()); + + TInt error = iProcessor->WriteVideoFrameToFile(ptr, + iDelayedTimeStamp, 0 /*dummy*/, + iDelayedKeyframe, EFalse, EFalse, EFalse ); + + if (error == KErrCompletion) + { + PRINT((_L("CVideoProcessor::WriteDelayedFrameL() write delayed frame, processing complete"))); + VDASSERT(iTranscoderStarted, 51); + iTransCoder->StopL(); + iTranscoderStarted = EFalse; + iFrameInfoArray.Reset(); + iTimer->CancelTimer(); + iProcessingComplete = ETrue; + } + else if (error != KErrNone) + { + User::Leave(error); + } + + // save frame number + iLastWrittenFrameNumber = iDelayedFrameNumber; + + delete iDelayedBuffer; + iDelayedBuffer = 0; + iDelayedWrite = EFalse; + + PRINT((_L("CVideoProcessor::WriteDelayedFrameL() end"))); + +} + +TInt CVideoProcessor::SetVideoFrameSize(TSize /*aSize*/) +{ + + PRINT((_L("CVideoProcessor::SetVideoFrameSize()"))) + + return KErrNone; +} + +TInt CVideoProcessor::SetAverageVideoBitRate(TInt /*aBitRate*/) +{ + + PRINT((_L("CVideoProcessor::SetAverageVideoBitRate()"))) + + return KErrNone; +} + + +TInt CVideoProcessor::SetMaxVideoBitRate(TInt /*aBitRate*/) +{ + PRINT((_L("CVideoProcessor::SetMaxVideoBitRate()"))) + + return KErrNone; +} + +TInt CVideoProcessor::SetAverageAudioBitRate(TInt /*aBitRate*/) +{ + PRINT((_L("CVideoProcessor::SetAverageAudioBitRate()"))) + + return KErrNone; +} + +// --------------------------------------------------------- +// CVideoProcessor::SetVideoCodecL() +// Interpret and store video mime type +// --------------------------------------------------------- +// +void CVideoProcessor::SetOutputVideoCodecL(const TPtrC8& aMimeType) + { + TBuf8<256> string; + TBuf8<256> newMimeType; + string = KVedMimeTypeH263; + string += _L8( "*" ); + + iMaxOutputFrameRate = 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 ) + { + iMaxOutputBitRate = iOutputBitRate = KVedBitRateH263Level10; + iMaxOutputResolution = KVedResolutionQCIF; + //dataBufferSize = KMaxCodedPictureSizeQCIF; + newMimeType += _L8( "; level=10" ); + } + else if ( aMimeType.MatchF( _L8("*level=45*") ) != KErrNotFound ) + { + iMaxOutputBitRate = iOutputBitRate = KVedBitRateH263Level45; + iMaxOutputResolution = 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 + iMaxOutputBitRate = iOutputBitRate = KVedBitRateH263Level10; + iMaxOutputResolution = 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 + iMaxOutputBitRate = iOutputBitRate = KVedBitRateMPEG4Level0; + iMaxOutputResolution = 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 + iMaxOutputBitRate = iOutputBitRate = KVedBitRateMPEG4Level0; + iMaxOutputResolution = 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 + iMaxOutputBitRate = iOutputBitRate = KVedBitRateMPEG4Level0; + iMaxOutputResolution = 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; + iMaxOutputResolution = KVedResolutionCIF; + iMaxOutputBitRate = iOutputBitRate = 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; + iMaxOutputBitRate = iOutputBitRate = KVedBitRateMPEG4Level2; + iMaxOutputResolution = KVedResolutionCIF; + iMaxOutputFrameRate = 30.0; + iArbitrarySizeAllowed = ETrue; + newMimeType += _L8("; profile-level-id=3"); + } + else if ( aMimeType.MatchF( _L8("*profile-level-id=4*") ) != KErrNotFound ) + { + // simple profile level 4a + iMaxOutputBitRate = iOutputBitRate = KVedBitRateMPEG4Level4A; + //dataBufferSize = KMaxCodedPictureSizeVGA; + iMaxOutputResolution = KVedResolutionVGA; + iMaxOutputFrameRate = 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) + iMaxOutputBitRate = iOutputBitRate = KVedBitRateMPEG4Level0; + iMaxOutputResolution = 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 + iMaxOutputBitRate = iOutputBitRate = KVedBitRateAVCLevel1; + iMaxOutputResolution = KVedResolutionQCIF; + newMimeType += _L8("; profile-level-id=42800A"); + } + else if ( aMimeType.MatchF( _L8("*profile-level-id=42900B*") ) != KErrNotFound ) + { + // baseline profile level 1b + iMaxOutputBitRate = iOutputBitRate = KVedBitRateAVCLevel1b; + iMaxOutputResolution = KVedResolutionQCIF; + newMimeType += _L8("; profile-level-id=42900B"); + } + else if ( aMimeType.MatchF( _L8("*profile-level-id=42800B*") ) != KErrNotFound ) + { + // baseline profile level 1.1 + iMaxOutputBitRate = iOutputBitRate = KVedBitRateAVCLevel1_1; + iMaxOutputResolution = KVedResolutionCIF; + newMimeType += _L8("; profile-level-id=42800B"); + } + else if ( aMimeType.MatchF( _L8("*profile-level-id=42800C*") ) != KErrNotFound ) + { + // baseline profile level 1.2 + iMaxOutputBitRate = iOutputBitRate = KVedBitRateAVCLevel1_2; + iMaxOutputResolution = KVedResolutionCIF; + newMimeType += _L8("; profile-level-id=42800C"); + } + //WVGA task + else if ( aMimeType.MatchF( _L8("*profile-level-id=42801E*") ) != KErrNotFound ) + { + // baseline profile level 3.0 + iMaxOutputBitRate = iOutputBitRate = KVedBitRateAVCLevel3; + iMaxOutputResolution = KVedResolutionWVGA; + newMimeType += _L8("; profile-level-id=42801E"); + } + + else if ( aMimeType.MatchF( _L8("*profile-level-id=42801F*") ) != KErrNotFound ) + { + // baseline profile level 3.1 + iMaxOutputBitRate = iOutputBitRate = KVedBitRateAVCLevel3_1; + iMaxOutputResolution = KVedResolutionWVGA; + 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 (?) + iMaxOutputBitRate = iOutputBitRate = KVedBitRateAVCLevel1; + iMaxOutputResolution = KVedResolutionQCIF; + 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 + iOutputMimeType = newMimeType; + + /*if ( iDataBuffer ) + { + delete iDataBuffer; + iDataBuffer = NULL; + } + iDataBuffer = (HBufC8*) HBufC8::NewL(dataBufferSize); */ + + } + +// --------------------------------------------------------- +// CVideoProcessor::GetVosHeaderSize() +// Gets the size of MPEG-4 VOS header (from encoder) +// --------------------------------------------------------- +// +TInt CVideoProcessor::GetVosHeaderSize() +{ + VDASSERT(iOutputVolHeader, 190); + + return iOutputVolHeader->Length(); +} + +// --------------------------------------------------------- +// CCallbackTimer::NewL() +// Two-phased constructor +// --------------------------------------------------------- +// +CCallbackTimer* CCallbackTimer::NewL(MTimerObserver& aObserver) +{ + + CCallbackTimer* self = new (ELeave) CCallbackTimer(aObserver); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + + return self; +} + +// --------------------------------------------------------- +// CCallbackTimer::CCallbackTimer() +// C++ default constructor. +// --------------------------------------------------------- +// +CCallbackTimer::CCallbackTimer(MTimerObserver& aObserver) : + CActive(EPriorityStandard), iObserver(aObserver) + +{ +} + +// --------------------------------------------------------- +// CCallbackTimer::~CCallbackTimer() +// Destructor +// --------------------------------------------------------- +// +CCallbackTimer::~CCallbackTimer() +{ + Cancel(); + + if ( iTimerCreated ) + { + iTimer.Close(); + iTimerCreated = EFalse; + } +} + +// --------------------------------------------------------- +// CCallbackTimer::ConstructL() +// Symbian 2nd phase constructor +// --------------------------------------------------------- +// +void CCallbackTimer::ConstructL() +{ + // Create a timer + User::LeaveIfError(iTimer.CreateLocal()); + iTimerCreated = ETrue; + + // Add us to active scheduler + CActiveScheduler::Add(this); +} + +// --------------------------------------------------------- +// CCallbackTimer::SetTimer() +// Set timer +// --------------------------------------------------------- +// +void CCallbackTimer::SetTimer(TTimeIntervalMicroSeconds32 aDuration) +{ + +// __ASSERT_DEBUG(!iTimerRequestPending != 0, -5000); //CSI: #174-D: expression has no effect, just an assert debug no effect intended +// __ASSERT_DEBUG(iTimerCreated, -5001); + + PRINT((_L("CCallbackTimer::SetTimer()"))) + + // activate timer to wait for encoding + SetActive(); + iStatus = KRequestPending; + iTimer.After(iStatus, aDuration); + iTimerRequestPending = ETrue; + +} + +// --------------------------------------------------------- +// CCallbackTimer::CancelTimer() +// Cancel timer +// --------------------------------------------------------- +// +void CCallbackTimer::CancelTimer() +{ + PRINT((_L("CCallbackTimer::CancelTimer()"))) + Cancel(); +} + +// --------------------------------------------------------- +// CCallbackTimer::RunL() +// AO running method +// --------------------------------------------------------- +// +void CCallbackTimer::RunL() +{ + if ( iTimerRequestPending ) + { + iTimerRequestPending = EFalse; + + // call observer + iObserver.MtoTimerElapsed(KErrNone); + } +} + +// --------------------------------------------------------- +// CCallbackTimer::DoCancel() +// AO cancelling method +// --------------------------------------------------------- +// +void CCallbackTimer::DoCancel() +{ + + // Cancel our timer request if we have one + if ( iTimerRequestPending ) + { + iTimer.Cancel(); + iTimerRequestPending = EFalse; + return; + } + +} + +// --------------------------------------------------------- +// CCallbackTimer::RunError() +// AO RunL error method +// --------------------------------------------------------- +// +TInt CCallbackTimer::RunError(TInt aError) +{ + Cancel(); + + // call observer + iObserver.MtoTimerElapsed(aError); + + return KErrNone; +} + + +// End of File +