/*
* 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 <e32svr.h>
#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<TUint8*>(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<TUint8*>(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<TUint8*>(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<TUint8*>(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<TUint8*>(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<TUint8*>(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<TUint8*>(aPicture->iRawData->Ptr()),
0 /* repeatFrame */, iFrameInfoArray[aIndex].iTransitionFrameNumber);
}
else
{
// Target frame is the one read from file, iColorTransitionBuffer
ApplySlidingTransitionEffect( iColorTransitionBuffer, const_cast<TUint8*>(aPicture->iRawData->Ptr()), (TVedMiddleTransitionEffect)iStartOfClipTransition,
0 /* repeatFrame */, iFrameInfoArray[aIndex].iTransitionFrameNumber);
}
// copy frame from edited buffer to transcoder buffer
Mem::Copy( const_cast<TUint8*>(aPicture->iRawData->Ptr()), iColorTransitionBuffer, yuvLength );
}
}
else
{
// repeatFrame
if ( iStartOfClipTransition == (TInt)EVedMiddleTransitionEffectCrossfade )
{
ApplyBlendingTransitionEffect( iOrigPreviousYUVBuffer, const_cast<TUint8*>(aPicture->iRawData->Ptr()),
1 /* repeatFrame */, iFrameInfoArray[aIndex].iTransitionFrameNumber);
}
else
{
ApplySlidingTransitionEffect( iOrigPreviousYUVBuffer, const_cast<TUint8*>(aPicture->iRawData->Ptr()), (TVedMiddleTransitionEffect)iStartOfClipTransition,
1 /* repeatFrame */, iFrameInfoArray[aIndex].iTransitionFrameNumber );
}
// copy frame from edited buffer to transcoder buffer
Mem::Copy( const_cast<TUint8*>(aPicture->iRawData->Ptr()), iOrigPreviousYUVBuffer, yuvLength );
}
}
else
{
// apply transition effect in spatial domain (to yuv data in encoder buffer)
// Do fading transition
ApplyFadingTransitionEffect(const_cast<TUint8*>(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<ySize; i+=pixelSkips, aYUVDataPtr+=pixelSkips, numberOfSamples++)
{
runningSum += *aYUVDataPtr;
if(*aYUVDataPtr > 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<TUint8*>(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<TUint8*>(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<TUint8*>(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<startFrameIndex)
endFrameIndex = startFrameIndex;
// determine the total number of included frames in the clip
iNumberOfIncludedFrames = endFrameIndex-startFrameIndex+1;
// make sure there are enough frames to apply transition
// for transition at both ends
if(iStartOfClipTransition && iEndOfClipTransition)
{
if ( iStartTransitionColor == EColorTransition &&
iEndTransitionColor == 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();
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<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable1[(*yFrame)>>5] + value);
value = 113; // 90% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable1[(*uFrame)>>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<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable2[(*yFrame)>>4] + value);
value = 98; // 80% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable2[(*uFrame)>>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<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable3[(*yFrame)>>4] + value);
value = 86; // 70% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable3[(*uFrame)>>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<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable4[(*yFrame)>>3] + value);
value = 72; //77; // 60% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable4[(*uFrame)>>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<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable5[(*yFrame)>>3] + value);
value = 62; // 50% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable5[(*uFrame)>>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<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable6[(*yFrame)>>3] + value);
value = 44; //51; // 40% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable6[(*uFrame)>>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<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable7[(*yFrame)>>2] + value);
value = 28; //38; // 30% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable7[(*uFrame)>>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<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable8[(*yFrame)>>2] + value);
value = 18; //25; // 20% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable8[(*uFrame)>>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<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable9[(*yFrame)>>2] + value);
value = 8; //13; // 10% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable9[(*uFrame)>>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<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable1[(*yFrame)>>5]);
value = 113; // 90% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable1[(*uFrame)>>5] + value);
*vFrame = (TUint8)(quantTable1[(*vFrame)>>5] + value);
}
break;
case 8: // 20% frame1, 80% frame2
for(i=0; i<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable2[(*yFrame)>>4]);
value = 98; // 80% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable2[(*uFrame)>>4] + value);
*vFrame = (TUint8)(quantTable2[(*vFrame)>>4] + value);
}
break;
case 7: // 30% frame1, 70% frame2
for(i=0; i<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable3[(*yFrame)>>4]);
value = 86; // 70% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable3[(*uFrame)>>4] + value);
*vFrame = (TUint8)(quantTable3[(*vFrame)>>4] + value);
}
break;
case 6: // 40% frame1, 60% frame2
for(i=0; i<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable4[(*yFrame)>>3]);
value = 72; //77; // 60% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable4[(*uFrame)>>3] + value);
*vFrame = (TUint8)(quantTable4[(*vFrame)>>3] + value);
}
break;
case 5: // 50% frame1, 50% frame2
for(i=0; i<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable5[(*yFrame)>>3]);
value = 62; // 50% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable5[(*uFrame)>>3] + value);
*vFrame = (TUint8)(quantTable5[(*vFrame)>>3] + value);
}
break;
case 4: // 60% frame1, 40% frame2
for(i=0; i<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable6[(*yFrame)>>3]);
value = 44; //51; // 40% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable6[(*uFrame)>>3] + value);
*vFrame = (TUint8)(quantTable6[(*vFrame)>>3] + value);
}
break;
case 3: // 70% frame1, 30% frame2
for(i=0; i<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable7[(*yFrame)>>2]);
value = 28; //38; // 30% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable7[(*uFrame)>>2] + value);
*vFrame = (TUint8)(quantTable7[(*vFrame)>>2] + value);
}
break;
case 2: // 80% frame1, 20% frame2
for(i=0; i<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable8[(*yFrame)>>2]);
value = 18; //25; // 20% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable8[(*uFrame)>>2] + value);
*vFrame = (TUint8)(quantTable8[(*vFrame)>>2] + value);
}
break;
case 1: // 90% frame1, 10% frame2
for(i=0; i<yLength; i++, yFrame++) // Y
*yFrame = (TUint8)(quantTable9[(*yFrame)>>2]);
value = 8; //13; // 10% of 128
for(i=0; i<uLength; i++, uFrame++,vFrame++) // U
{
*uFrame = (TUint8)(quantTable9[(*uFrame)>>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<yLength; i++, yFrame1++,yFrame2++) // Y
*yFrame1 = (TUint8)(quantTable1[(*yFrame1)>>5] + quantTable9[(*yFrame2)>>2]);
for(i=0; i<uLength; i++, uFrame1++,uFrame2++,vFrame1++,vFrame2++) // U
{
*uFrame1 = (TUint8)(quantTable1[(*uFrame1)>>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<yLength; i++, yFrame1++,yFrame2++) // Y
*yFrame1 = (TUint8)(quantTable2[(*yFrame1)>>4] + quantTable8[(*yFrame2)>>2]);
for(i=0; i<uLength; i++, uFrame1++,uFrame2++,vFrame1++,vFrame2++) // U
{
*uFrame1 = (TUint8)(quantTable2[(*uFrame1)>>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<yLength; i++, yFrame1++,yFrame2++) // Y
*yFrame1 = (TUint8)(quantTable3[(*yFrame1)>>4] + quantTable7[(*yFrame2)>>2]);
for(i=0; i<uLength; i++, uFrame1++,uFrame2++,vFrame1++,vFrame2++) // U
{
*uFrame1 = (TUint8)(quantTable3[(*uFrame1)>>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<yLength; i++, yFrame1++,yFrame2++) // Y
*yFrame1 = (TUint8)(quantTable4[(*yFrame1)>>3] + quantTable6[(*yFrame2)>>3]);
for(i=0; i<uLength; i++, uFrame1++,uFrame2++,vFrame1++,vFrame2++) // U
{
*uFrame1 = (TUint8)(quantTable4[(*uFrame1)>>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<yLength; i++, yFrame1++,yFrame2++) // Y
*yFrame1 = (TUint8)(quantTable5[(*yFrame1)>>3] + quantTable5[(*yFrame2)>>3]);
for(i=0; i<uLength; i++, uFrame1++,uFrame2++,vFrame1++,vFrame2++) // U
{
*uFrame1 = (TUint8)(quantTable5[(*uFrame1)>>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<yLength; i++, yFrame1++,yFrame2++) // Y
*yFrame1 = (TUint8)(quantTable6[(*yFrame1)>>3] + quantTable4[(*yFrame2)>>3]);
for(i=0; i<uLength; i++, uFrame1++,uFrame2++,vFrame1++,vFrame2++) // U
{
*uFrame1 = (TUint8)(quantTable6[(*uFrame1)>>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<yLength; i++, yFrame1++,yFrame2++) // Y
*yFrame1 = (TUint8)(quantTable7[(*yFrame1)>>2] + quantTable3[(*yFrame2)>>4]);
for(i=0; i<uLength; i++, uFrame1++,uFrame2++,vFrame1++,vFrame2++) // U
{
*uFrame1 = (TUint8)(quantTable7[(*uFrame1)>>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<yLength; i++, yFrame1++,yFrame2++) // Y
*yFrame1 = (TUint8)(quantTable8[(*yFrame1)>>2] + quantTable2[(*yFrame2)>>4]);
for(i=0; i<uLength; i++, uFrame1++,uFrame2++,vFrame1++,vFrame2++) // U
{
*uFrame1 = (TUint8)(quantTable8[(*uFrame1)>>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<yLength; i++, yFrame1++,yFrame2++) // Y
*yFrame1 = (TUint8)(quantTable9[(*yFrame1)>>2] + quantTable1[(*yFrame2)>>5]);
for(i=0; i<uLength; i++, uFrame1++,uFrame2++,vFrame1++,vFrame2++) // U
{
*uFrame1 = (TUint8)(quantTable9[(*uFrame1)>>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; i<yHeight; i++, yFrame1+=yWidth,yFrame2+=yWidth)
{
ptr.Set(yFrame1,ySliceWidth,ySliceWidth);
ptr.Copy(yFrame2,ySliceWidth);
}
// u,v
for(i=0; i<uHeight; i++, uFrame1+=uWidth,uFrame2+=uWidth,vFrame1+=uWidth,vFrame2+=uWidth)
{
ptr.Set(uFrame1,uSliceWidth,uSliceWidth);
ptr.Copy(uFrame2,uSliceWidth);
ptr.Set(vFrame1,uSliceWidth,uSliceWidth);
ptr.Copy(vFrame2,uSliceWidth);
}
break;
case EVedMiddleTransitionEffectWipeRightToLeft:
// figure out the amount of data to change
VDASSERT(iStartNumberOfTransitionFrames,20);
ySliceWidth = (TInt)((TReal)yWidth * (TReal)(frameNumber+1)/(TReal)(iStartNumberOfTransitionFrames<<1) + 0.5);
if(ySliceWidth>yWidth) 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; i<yHeight; i++, yFrame1+=yWidth,yFrame2+=yWidth)
{
ptr.Set(yFrame1,ySliceWidth,ySliceWidth);
ptr.Copy(yFrame2,ySliceWidth);
}
// u,v
for(i=0; i<uHeight; i++, uFrame1+=uWidth,uFrame2+=uWidth,vFrame1+=uWidth,vFrame2+=uWidth)
{
ptr.Set(uFrame1,uSliceWidth,uSliceWidth);
ptr.Copy(uFrame2,uSliceWidth);
ptr.Set(vFrame1,uSliceWidth,uSliceWidth);
ptr.Copy(vFrame2,uSliceWidth);
}
break;
case EVedMiddleTransitionEffectWipeTopToBottom:
// figure out the amount of data to change
VDASSERT(iStartNumberOfTransitionFrames,21);
ySliceWidth = (TInt)((TReal)yHeight * (TReal)(frameNumber+1)/(TReal)(iStartNumberOfTransitionFrames<<1) + 0.5);
if(ySliceWidth>yHeight) 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<TUint8*>(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<TUint8*>(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<TUint8*>(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<TUint8*>(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<TUint8*>(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<TUint8*>(tempBuffer->Des().Ptr());
TUint8* src = const_cast<TUint8*>(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