diff -r 000000000000 -r 71ca22bcf22a mmserv/thumbnailengine/TneProcessorSrc/TNEProcessorImpl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmserv/thumbnailengine/TneProcessorSrc/TNEProcessorImpl.cpp Tue Feb 02 01:08:46 2010 +0200 @@ -0,0 +1,2071 @@ +/* +* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Implementation for TNE processor +* +*/ + + + +// Include Files +#include "TNEProcessorImpl.h" +#include "yuv2rgb12.h" +#include "yuv2rgb16.h" +#include "yuv2rgb24.h" +#include "DisplayChain.h" +#include "TNEVideosettings.h" +#include "ctrsettings.h" +#include "mp4parser.h" +#include "TNEDecoderWrap.h" + +// Local Constants +const TUint KReadBufInitSize = 512; // stream start buffer initial size +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 + +// An assertion macro wrapper to clean up the code a bit +#define VPASSERT(x) __ASSERT_DEBUG(x, User::Panic(_L("CTNEProcessorImpl"), EInvalidInternalState)) + +// An assertion macro wrapper to clean up the code a bit +#define VDASSERT(x, n) __ASSERT_DEBUG(x, User::Panic(_L("CTNEProcessorImpl"), EInternalAssertionFailure+n)) + +#ifdef _DEBUG +#include +#define PRINT(x) RDebug::Print x; +#else +#define PRINT(x) +#endif + +// ================= MEMBER FUNCTIONS ======================= + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::NewL +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +CTNEProcessorImpl* CTNEProcessorImpl::NewL() +{ + CTNEProcessorImpl* self = NewLC(); + CleanupStack::Pop(self); + return self; +} + +CTNEProcessorImpl* CTNEProcessorImpl::NewLC() +{ + CTNEProcessorImpl* self = new (ELeave) CTNEProcessorImpl(); + CleanupStack::PushL(self); + self->ConstructL(); + return self; +} + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::CTNEProcessorImpl +// C++ default constructor can NOT contain any code, that +// might leave. +// ----------------------------------------------------------------------------- +// +CTNEProcessorImpl::CTNEProcessorImpl() +: CActive(EPriorityNormal), iReadDes(0, 0) +{ + // Reset state + iState = EStateIdle; + iFileFormat = EDataAutoDetect; +} + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::~CTNEProcessorImpl +// Destructor. +// ----------------------------------------------------------------------------- +// +CTNEProcessorImpl::~CTNEProcessorImpl() +{ + Cancel(); + + TInt error = KErrNone; + + TRAP(error, DoCloseVideoL()); + + if(iParser) + { + delete iParser; + iParser = 0; + } + + // Deallocate buffers + // This is allocated in the clip startFrame() method + if (iFrameBuffer) + { + User::Free(iFrameBuffer); + iFrameBuffer = 0; + } + + // Deallocate buffers + if (iDataBuffer) + { + User::Free(iDataBuffer); + iDataBuffer = 0; + } + + if (iRgbBuf) + { + delete iRgbBuf; + iRgbBuf = 0; + } + + if (iOutBitmap) + { + delete iOutBitmap; + iOutBitmap = 0; + } + + if (iReadBuf) + { + User::Free(iReadBuf); + iReadBuf = 0; + } + + if (iMediaBuffer) + { + delete iMediaBuffer; + iMediaBuffer = 0; + } + +} + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::ConstructL +// Symbian 2nd phase constructor can leave. +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::ConstructL() +{ + // Add to active scheduler + CActiveScheduler::Add(this); + + // Allocate buffer + iDataBuffer = (TUint8*) User::AllocL(KInitialDataBufferSize); + iBufferLength = KInitialDataBufferSize; + + // Allocate stream reading buffer + iReadBuf = (TUint8*) User::AllocL(KReadBufInitSize); + iBufLength = KReadBufInitSize; + + iMediaBuffer = new (ELeave)TVideoBuffer; + + iDecodePending = EFalse; + iDecoding = EFalse; + + // Flag to indicate if the frame has been decoded + iThumbFrameDecoded = EFalse; + + iState = EStateIdle; +} + + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::Reset +// Resets the processor for processing a new movie +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::Reset() +{ + + iFileFormat = EDataAutoDetect; + iVideoType = EVideoH263Profile0Level10; + iFirstFrameOfClip = EFalse; + iFirstFrameFlagSet = EFalse; + + iProcessingCancelled = EFalse; + + iWaitSchedulerStarted = EFalse; + + iVideoFrameNumber = 0; + iFrameBuffered = EFalse; + iVideoIntraFrameNumber = 0; + iStartThumbIndex = 0; + iVideoClipDuration = 0; + iDecoderInitPending = EFalse; + + // @@ YHK HARI AVC frame number flag + // need to have a more inclusive approach for + // all the different encoded streams + iAVCDecodedFrameNumber = 0; + + // We are now properly initialized + iState = EStateIdle; +} + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::GetClipPropertiesL +// Retrieves parameters for a clip +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::GetClipPropertiesL(RFile& aFileHandle, + TTNEVideoFormat& aFormat, + TTNEVideoType& aVideoType, + TSize& aResolution, + TInt& aVideoFrameCount) +{ + PRINT((_L("CTNEProcessorImpl::GetClipPropertiesL() begin"))) + + // parse clip header + CParser::TStreamParameters iStreamParams; + + // parse header + TRAPD(error, ParseHeaderOnlyL(iStreamParams, aFileHandle)); + + if (error != KErrNone && error != KErrNotSupported) + User::Leave(error); + + /* pass back clip properties */ + + // video format (file format actually) + if (iStreamParams.iFileFormat == CParser::EFileFormat3GP) + aFormat = ETNEVideoFormat3GPP; + else if (iStreamParams.iFileFormat == CParser::EFileFormatMP4) + aFormat = ETNEVideoFormatMP4; + else + aFormat = ETNEVideoFormatUnrecognized; + + // video type + if(iStreamParams.iVideoFormat == CParser::EVideoFormatNone) + aVideoType = ETNEVideoTypeNoVideo; + else if (iStreamParams.iVideoFormat == CParser::EVideoFormatH263Profile0Level10) + aVideoType = ETNEVideoTypeH263Profile0Level10; + else if (iStreamParams.iVideoFormat == CParser::EVideoFormatH263Profile0Level45) + aVideoType = ETNEVideoTypeH263Profile0Level45; + else if(iStreamParams.iVideoFormat == CParser::EVideoFormatMPEG4) + aVideoType = ETNEVideoTypeMPEG4SimpleProfile; + else if(iStreamParams.iVideoFormat == CParser::EVideoFormatAVCProfileBaseline) + aVideoType = ETNEVideoTypeAVCProfileBaseline; + else + aVideoType = ETNEVideoTypeUnrecognized; + + // resolution + aResolution.iWidth = iStreamParams.iVideoWidth; + aResolution.iHeight = iStreamParams.iVideoHeight; + + // get total number of video frames + aVideoFrameCount = iParser->GetNumberOfVideoFrames(); + + PRINT((_L("CTNEProcessorImpl::GetClipPropertiesL() end"))) +} + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::StartThumbL +// Initiates thumbnail extraction from clip (full resolution raw is reutrned) +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::StartThumbL(RFile& aFileHandle, + TInt aIndex, + TSize aResolution, + TDisplayMode aDisplayMode, + TBool aEnhance) +{ + PRINT((_L("CTNEProcessorImpl::StartThumbL() begin, aIndex = %d, enhance = %d"), aIndex, aEnhance)) + + // Get thumbnail parameters + iOutputThumbResolution.SetSize(aResolution.iWidth, aResolution.iHeight); + iThumbIndex = aIndex; + iThumbDisplayMode = aDisplayMode; + iThumbEnhance = aEnhance; + + // opens the file & parses header + InitializeClipL(aFileHandle); + + // Allocate memory for iFrameBuffer + TInt length = iVideoParameters.iWidth * iVideoParameters.iHeight; + length += (length>>1); + iFrameBuffer = (TUint8*)User::AllocL(length); + + PRINT((_L("CTNEProcessorImpl::StartThumbL() end"))) +} + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::ProcessThumbL +// Generates thumbnail from clip (actually, full resolution raw is returned) +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::ProcessThumbL(TRequestStatus &aStatus) +{ + PRINT((_L("CTNEProcessorImpl::ProcessThumbL() begin"))) + + iState = EStateProcessing; + iThumbnailRequestStatus = &aStatus; + + // seek to the last intra frame before desired frame + TTimeIntervalMicroSeconds startTime(0); + if ( iThumbIndex > 0 ) + { + TInt time = 0; + TUint inMs = TUint( iParser->GetVideoFrameStartTime(iThumbIndex, &time) ); + TInt64 inMicroS = TInt64( inMs ) * TInt64( 1000 ); + startTime = TTimeIntervalMicroSeconds( inMicroS ); + } + + + TInt error = iParser->SeekOptimalIntraFrame(startTime, iThumbIndex); + if (error != KErrNone) + { + iThumbnailRequestStatus = 0; + User::Leave(KErrGeneral); + } + + iStartThumbIndex = iParser->GetStartFrameIndex(); + + // @@ YHK: Try to handle this more inclusively + iAVCDecodedFrameNumber = iStartThumbIndex; + + VPASSERT(iStartThumbIndex >= 0); + + // determine input stream type + TRAP(error, GetFrameL()); + + iDecoding = ETrue; + + // Create and Initialize the Decoders + TRAP(error, CreateAndInitializeDecoderL()); + if (error != KErrNone) + { + // @@ YHK Do we want to use this flag ?? + iThumbnailRequestStatus = 0; + User::Leave(KErrGeneral); + } +} + +// --------------------------------------------------------- +// CH263Decoder::DecodeThumb +// Decode a thumbnail frame internally +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CTNEProcessorImpl::DecodeThumb(TBool aFirstFrame) +{ + + if (aFirstFrame) + { + // frame read in iDataBuffer, decode + TVideoBuffer::TBufferType bt = + (iDataFormat == EDataH263) ? TVideoBuffer::EVideoH263 : TVideoBuffer::EVideoMPEG4; + + TInt startTimeInTicks=0; + TInt startTimeInMs = 0; + startTimeInMs = iParser->GetVideoFrameStartTime(iStartThumbIndex,&startTimeInTicks); + + TTimeIntervalMicroSeconds ts = + TTimeIntervalMicroSeconds(startTimeInMs * TInt64(1000) ); + + iMediaBuffer->Set( TPtrC8(iDataBuffer, iBufferLength), + bt, + iCurrentFrameLength, + ETrue, // keyFrame + ts + ); + + iPreviousTimeStamp = ts; + + iDecodePending = ETrue; + + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + + TRAPD( err, iDecoder->WriteCodedBufferL(iMediaBuffer) ); + if (err != KErrNone) + { + // ready + MNotifyThumbnailReady(err); + return; + } + + TUint freeInputBuffers = iDecoder->GetNumInputFreeBuffers(); + + // @@ YHK: AVC hack is there a better way of doing this ?? + if(iDataFormat == EDataAVC) + { + if (freeInputBuffers != 0) + { + // activate object to end processing + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + } + } + return; + } + + if ((iThumbIndex == 0) && iThumbFrameDecoded) + { + MNotifyThumbnailReady(KErrNone); + return; + } + + iStartThumbIndex++; + + if ((iThumbIndex < 0) && iThumbFrameDecoded) + { + if (iFramesToSkip == 0) + { + PRINT((_L("CH263Decoder::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 + MNotifyThumbnailReady(KErrNone); + return; + } + iFramesToSkip = 10; + iNumThumbFrameSkips++; + } + else + iFramesToSkip--; + + // read new frame & decode + } + + if (iThumbFrameDecoded) + { + iAVCDecodedFrameNumber++; + iThumbFrameDecoded = EFalse; + } + + + if (iThumbIndex > 0) + { + // HARI AVC IMP THUMB + TInt decodedFrameNumber = (iDataFormat == EDataAVC) ? iAVCDecodedFrameNumber : iStartThumbIndex; + + if (decodedFrameNumber > iThumbIndex) + { + // ready + MNotifyThumbnailReady(KErrNone); + return; + } + // read new frame & decode + } + + TInt error; + + if(iStartThumbIndex < iParser->GetNumberOfVideoFrames()) // do not read last frame (already read!) + { + error = ReadVideoFrame(); + if (error != KErrNone) + { + MNotifyThumbnailReady(error); + return; + } + } + else + { + // no frames left, return + MNotifyThumbnailReady(KErrNone); + return; + } + + iCurrentFrameLength = 0; + iDataFormat = EDataUnknown; + + if (ReadAndUpdateFrame()) + { + // frame read in iDataBuffer, decode + TVideoBuffer::TBufferType bt = + (iDataFormat == EDataH263) ? TVideoBuffer::EVideoH263 : TVideoBuffer::EVideoMPEG4; + + TInt startTimeInTicks=0; + TInt startTimeInMs = 0; + startTimeInMs = iParser->GetVideoFrameStartTime(iStartThumbIndex,&startTimeInTicks); + + TTimeIntervalMicroSeconds ts = + TTimeIntervalMicroSeconds(startTimeInMs * TInt64(1000) ); + + if (ts <= iPreviousTimeStamp) + { + // adjust timestamp so that its bigger than ts of previous frame + TReal frameRate = GetVideoClipFrameRate(); + + 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; + + iMediaBuffer->Set( TPtrC8(iDataBuffer, iBufferLength), + bt, + iCurrentFrameLength, + GetVideoFrameType(iStartThumbIndex), + ts ); + + iDecodePending = ETrue; + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + + TRAPD( err, iDecoder->WriteCodedBufferL(iMediaBuffer) ); + if (err != KErrNone) + { + MNotifyThumbnailReady(err); + } + + TUint freeInputBuffers = iDecoder->GetNumInputFreeBuffers(); + + // HARI AVC hack is there a better way of doing this ?? + if(iDataFormat == EDataAVC) + { + if (freeInputBuffers != 0) + { + // activate object to end processing + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + } + } + + return; + } + else + { + MNotifyThumbnailReady(KErrCorrupt); + return; + } +} + +void CTNEProcessorImpl::MSendEncodedBuffer() +{ + DecodeThumb(EFalse); +} + + +void CTNEProcessorImpl::MPictureFromDecoder(TVideoPicture* aPicture) +{ + TInt yuvLength = iVideoParameters.iWidth*iVideoParameters.iHeight; + yuvLength += (yuvLength >> 1); + + // Indicate that the decoded frame has been received + iThumbFrameDecoded = ETrue; + + // copy to iFrameBuffer + Mem::Copy(iFrameBuffer, aPicture->iData.iRawData->Ptr(), yuvLength); + + // release picture + TInt error = KErrNone; + TRAP( error, iDecoder->ReturnPicture(aPicture) ); + if ( error != KErrNone ) + { + MNotifyThumbnailReady(error); + return; + } + + //VDASSERT(iDecodePending, 33); + // complete request + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + + return; +} + +void CTNEProcessorImpl::MReturnCodedBuffer(TVideoBuffer* /*aBuffer*/) +{ + // Don't have to do anything here + return; +} + +// --------------------------------------------------------- +// CTNEProcessorImpl::CreateAndInitializeDecoderL +// Create and initialize decoder +// (other items were commented in a header). +// --------------------------------------------------------- +// +void CTNEProcessorImpl::CreateAndInitializeDecoderL() +{ + PRINT((_L("CTNEProcessorImpl::CreateAndInitializeDecoderL() begin"))); + TTRVideoFormat videoInputFormat; + + // parse the mime type: Get the codeclevel, max bitrate etc... + ParseMimeTypeL(); + + // Create decoder wrapper object + iDecoder = CTNEDecoderWrap::NewL(this); + + // Check to see if this mime type is supported + if ( !(iDecoder->SupportsCodec(iMimeType, iShortMimeType)) ) + { + User::Leave(KErrNotSupported); + } + + videoInputFormat.iSize = TSize(iVideoParameters.iWidth, iVideoParameters.iHeight); + videoInputFormat.iDataType = ETRDuCodedPicture; + + // set the codec params + iDecoder->SetDecoderParametersL(iCodecLevel, videoInputFormat); + + iDecoderInitPending = ETrue; + + // Activate the processor object + if (!IsActive()) + { + SetActive(); + + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); + } + + // initialize the decoder + iDecoder->InitializeL(); + +} + +// ----------------------------------------------------------------------------- +// CTRTranscoderImp::ParseMimeTypeL +// Parses given MIME type +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::ParseMimeTypeL() +{ + TUint maxBitRate = 0; + TInt codecType = 0; + TBuf8<256> shortMimeType; + TBuf8<256> newMimeType; + TInt width = iVideoParameters.iWidth; + TUint codecLevel = 0; + + if ( iDataFormat == EDataH263 ) + { + // H.263 + codecType = EH263; + shortMimeType = _L8("video/H263-2000"); + newMimeType = shortMimeType; + + switch( width ) + { + case KTRSubQCIFWidth: + case KTRQCIFWidth: + { + // Set defaults for level=10; + maxBitRate = KTRMaxBitRateH263Level10; + codecLevel = KTRH263CodecLevel10; + newMimeType += _L8("; level=10"); + break; + } + + case KTRCIFWidth: + { + // Set defaults for level=30; + maxBitRate = KTRMaxBitRateH263Level30; + codecLevel = KTRH263CodecLevel30; + newMimeType += _L8("; level=30"); + break; + } + + case KTRPALWidth: + { + // Set defaults for level=60; + maxBitRate = KTRMaxBitRateH263Level60; + codecLevel = KTRH263CodecLevel60; + newMimeType += _L8("; level=60"); + break; + } + + default: + { + // Set defaults for level=10; + maxBitRate = KTRMaxBitRateH263Level10; + codecLevel = KTRH263CodecLevel10; + newMimeType += _L8("; level=10"); + break; + } + } + } + else if ( iDataFormat == EDataMPEG4 ) + { + // MPEG-4 Visual + codecType = EMpeg4; + shortMimeType = _L8("video/mp4v-es"); // Set short mime + newMimeType = shortMimeType; + + switch( width ) + { + case KTRSubQCIFWidth: + case KTRQCIFWidth: + { + // Set profile-level-id=0 + codecLevel = KTRMPEG4CodecLevel0; + maxBitRate = KTRMaxBitRateMPEG4Level0; + newMimeType += _L8("; profile-level-id=8"); + break; + } + + case KTRQVGAWidth: + case KTRCIFWidth: + { + // Set profile-level-id=3 + maxBitRate = KTRMaxBitRateMPEG4Level3; + codecLevel = KTRMPEG4CodecLevel3; + newMimeType += _L8("; profile-level-id=3"); + break; + } + + case KTRVGAWidth: + { + // Set profile-level-id=4 (4a) + maxBitRate = KTRMaxBitRateMPEG4Level4a; + codecLevel = KTRMPEG4CodecLevel4a; + newMimeType += _L8("; profile-level-id=4"); + break; + } + + default: + { + // Set profile-level-id=0 + maxBitRate = KTRMaxBitRateMPEG4Level0; + codecLevel = KTRMPEG4CodecLevel0; + newMimeType += _L8("; profile-level-id=8"); + break; + } + } + } + else if (iDataFormat == EDataAVC) + { + // @@ YHK this is a hack for AVC fix it later.... + // @@ YHK Imp ***************** + + codecType = EH264; + shortMimeType = _L8("video/H264"); // Set short mime + newMimeType = shortMimeType; + + codecLevel = KTRMPEG4CodecLevel0; + maxBitRate = KTRMaxBitRateMPEG4Level0; + newMimeType += _L8("; profile-level-id=428014"); + + } + else + { + PRINT((_L("CTRTranscoderImp::ParseMimeL(), there is curently no support for this type"))) + User::Leave(KErrNotSupported); + } + + // Mime type was set for Input format + iCodecLevel = codecLevel; + iCodec = codecType; + iMaxBitRate = maxBitRate; + + iMimeType = newMimeType; + iShortMimeType = shortMimeType; + +} + +// --------------------------------------------------------- +// CTNEProcessorImpl::GetFrameL +// Gets the transcode factor from the current clip +// (other items were commented in a header). +// --------------------------------------------------------- +// +TInt CTNEProcessorImpl::GetFrameL() +{ + // Read the video frame into buffer + TInt error = ReadVideoFrame(); + + // seek to and decode first frame + if (!ReadAndUpdateFrame()) + User::Leave(KErrCorrupt); + + return KErrNone; +} + + + +// --------------------------------------------------------- +// CTNEProcessorImpl::ReadVideoFrame +// Gets the transcode factor from the current clip +// (other items were commented in a header). +// --------------------------------------------------------- +// +TInt CTNEProcessorImpl::ReadVideoFrame() +{ + TUint frameLen; + TFrameType frameType = EFrameTypeVideo; + TBool frameAvailable = 0; + TPtr8 readDes(0,0); + TUint32 numReadFrames = 0; + TUint32 timeStamp; + + // Get the next frame information + TInt error = iParser->GetNextFrameInformation(frameType, + frameLen, + frameAvailable); + + if (error !=KErrNone) + return error; + + VPASSERT(frameAvailable); + + while (iBufferLength < frameLen) + { + // 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) + { + return EFalse; + } + + iDataBuffer = tmp; + iBufferLength = newSize; + } + + iDataLength = frameLen; + + + if (iBufferLength < iDataLength) + { + // need to allocate a bigger buffer + User::Panic(_L("CVideoPlayer"), EInvalidInternalState); + } + + + // @@ YHK clean up replace *p with the iDataBuffer directly + // make space for timestamp + TUint8 *p = iDataBuffer; + readDes.Set(p, 0, TInt(frameLen)); + + + // @@ YHK check frameType do we need to send it ?? + // is the parser smart enopugh to get the video frame + // type or do we need to skip the audio frame ??? + + // @@ YHK test this scenario wiht Nth frame + // and how do we read the frame from the stream + + // read frame(s) from parser + error = iParser->ReadFrames(readDes, frameType, + numReadFrames, timeStamp); + + if ( error != KErrNone ) + return error; + + VPASSERT( numReadFrames > 0 ); + + // @@ YHK We dont need the TS, its not used anywhere + // put timestamp in the output block before the actual frame data + // Mem::Copy(iDataBuffer, &timeStamp, 4); + + // set the frame length back to zero + // frameLen = 0; + + return KErrNone; +} + + +// --------------------------------------------------------- +// CTNEProcessorImpl::ReadAndUpdateFrame +// Read the encoded frame and Update information +// (other items were commented in a header). +// --------------------------------------------------------- +// +TInt CTNEProcessorImpl::ReadAndUpdateFrame() +{ + // Determine data format if needed + if ( iDataFormat == EDataUnknown ) + { + // OK, we have 4 bytes of data. Check if the buffer starts with a + // H.263 PSC: + + if(iParser->iStreamParameters.iVideoFormat == CParser::EVideoFormatAVCProfileBaseline) + { + iDataFormat = EDataAVC; + } + else 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("CH263Decoder::ReadFrame() - no PSC or MPEG-4 start code in the start of the buffer"))); + 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"), EInvalidInternalState); + } + + // 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 ((iCurrentFrameLength <= startCodeLength) && (iDataLength <= startCodeLength) ) + return EFalse; + } + else + { + if ( (iCurrentFrameLength <= startCodeLength) && (iDataLength < startCodeLength) ) + return EFalse; + } + + // When reading H.263, the buffer always starts with the PSC of the + // current frame + if (iDataFormat == EDataH263) + { + + // 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("CH263Decoder::ReadFrame() - no PSC in the start of the buffer"))) + 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 + + // If we are at the start of a block, check if it begins with a PSC + if ( (iDataLength > 2) && + ( (iDataBuffer[0] == 0) && (iDataBuffer[1] == 0) && ((iDataBuffer[2] & 0xfc) == 0x80) ) ) + { + gotPSC = ETrue; + iCurrentFrameLength = iDataLength; + } + else + { + PRINT((_L("CH263Decoder::ReadFrame() - no PSC in the start of the buffer"))) + return EFalse; + } + } + return ETrue; + } + else if (iDataFormat == EDataMPEG4) + { + // MPEG-4 + + // 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; + } + else + { + // Allocate buffer + // @@ YHK need to come up with some decent value other than 100 + TUint8* tmpPtr = (TUint8*) User::AllocL(iDataLength + 100); + TInt dLen = 0; + TInt skip = 0; + TUint32 nalSize = 0; + + if(iFirstFrameOfClip) + { + // Set the flag to false. + iFirstFrameOfClip = EFalse; + +/////////////////////////////////////////////////////////////////// +/* +AVC Decoder Configuration +------------------------- +aligned(8) class AVCDecoderConfigurationRecord { +unsigned int(8) configurationVersion = 1; +unsigned int(8) AVCProfileIndication; +unsigned int(8) profile_compatibility; +unsigned int(8) AVCLevelIndication; +bit(6) reserved = ‘111111’b; +unsigned int(2) lengthSizeMinusOne; +bit(3) reserved = ‘111’b; +unsigned int(5) numOfSequenceParameterSets; +for (i=0; i< numOfSequenceParameterSets; i++) { + unsigned int(16) sequenceParameterSetLength ; + bit(8*sequenceParameterSetLength) sequenceParameterSetNALUnit; +} +unsigned int(8) numOfPictureParameterSets; +for (i=0; i< numOfPictureParameterSets; i++) { + unsigned int(16) pictureParameterSetLength; + bit(8*pictureParameterSetLength) pictureParameterSetNALUnit; +} +*/ +////////////////////////////////////////////////////////////////////// + + // Copy the first 4 bytes for config version, profile indication + // profile compatibility and AVC level indication + Mem::Copy(&tmpPtr[dLen], iDataBuffer, 4); + dLen += 4; + skip += 4; + + // copy 1 byte for bit(6) reserved = ‘111111’b; + // unsigned int(2) lengthSizeMinusOne; + Mem::Copy(&tmpPtr[dLen], iDataBuffer + skip, 1); + dLen += 1; + + iNalUnitBytes = (0x3 & iDataBuffer[skip]) + 1; + skip += 1; + + // SSP packets + Mem::Copy(&tmpPtr[dLen], iDataBuffer + skip, 1); + dLen += 1; + + TInt numOfSSP = 0x1F & iDataBuffer[skip]; + skip += 1; + + for (TInt i = 0; i < numOfSSP; i++) + { + TInt sspSize = iDataBuffer[skip]*256 + iDataBuffer[skip+1]; + skip += 2; + + tmpPtr[dLen++] = 0; + tmpPtr[dLen++] = 0; + tmpPtr[dLen++] = 0; + tmpPtr[dLen++] = 0x01; + + Mem::Copy(&tmpPtr[dLen], iDataBuffer + skip, sspSize); + + skip += sspSize; + dLen += sspSize; + } + + // PSP packets + Mem::Copy(&tmpPtr[dLen], iDataBuffer + skip, 1); + dLen += 1; + + TInt numOfPSP = iDataBuffer[skip]; + skip += 1; + + for (TInt i = 0; i < numOfPSP; i++) + { + TInt pspSize = iDataBuffer[skip]*256 + iDataBuffer[skip+1]; + skip += 2; + + tmpPtr[dLen++] = 0; + tmpPtr[dLen++] = 0; + tmpPtr[dLen++] = 0; + tmpPtr[dLen++] = 0x01; + + Mem::Copy(&tmpPtr[dLen], iDataBuffer + skip, pspSize); + + skip += pspSize; + dLen += pspSize; + } + } + + while(skip < iDataLength) + { + switch (iNalUnitBytes) + { + case 1: + nalSize = iDataBuffer[skip]; + break; + case 2: + nalSize = iDataBuffer[skip]*256 + iDataBuffer[skip+1]; + break; + case 4: + nalSize = iDataBuffer[skip]*14336 + iDataBuffer[skip+1]*4096 + iDataBuffer[skip+2]*256 + iDataBuffer[skip+3]; + break; + } + + skip += iNalUnitBytes; + + tmpPtr[dLen++] = 0; + tmpPtr[dLen++] = 0; + tmpPtr[dLen++] = 0; + tmpPtr[dLen++] = 0x01; + + Mem::Copy(&tmpPtr[dLen], iDataBuffer + skip, nalSize); + + skip += nalSize; + dLen += nalSize; + } + + Mem::Copy(iDataBuffer, tmpPtr, dLen); + iDataLength = dLen; + iCurrentFrameLength = iDataLength; + + if(tmpPtr) + delete tmpPtr; + + // we have a complete frame + return ETrue; + + } + +} + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::MNotifyThumbnailReady +// Called by thumbnail generator when thumbnail is ready +// for retrieval +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::MNotifyThumbnailReady(TInt aError) +{ + // Handle if any error is returned + if (HandleThumbnailError(aError)) + return; + TInt bytesPerPixel = 0; + TInt error; + + if ( !iRgbBuf ) + { + TSize inputFrameResolution(iParser->iStreamParameters.iVideoWidth,iParser->iStreamParameters.iVideoHeight); + + // rgb specs + TUint thumbLength = inputFrameResolution.iWidth * inputFrameResolution.iHeight; + TUint thumbUVLength = thumbLength>>2; + + // VPASSERT(iYuvBuf); + // assign yuv pointers + TUint8* yBuf = iFrameBuffer; + TUint8* uBuf = yBuf + thumbLength; + TUint8* vBuf = uBuf + thumbUVLength; + + // check validity of thumbnail and associated operation + if(iThumbEnhance) // for saving to file + { + if(iThumbDisplayMode == ENone) // if no preference + iThumbDisplayMode = EColor16M; // 24-bit color image for enhancement + else if(iThumbDisplayMode != EColor16M) // invalid combination + { + HandleThumbnailError(KErrNotSupported); + return; + } + } + else // for screen display + { + if(iThumbDisplayMode == ENone) // if no preference + iThumbDisplayMode = EColor64K; // 16-bit image + } + + // determine proper bit depth for the bitmap + if(iThumbDisplayMode == EColor16M) + bytesPerPixel = 3; // 24-bit rgb takes 3 bytes, stored as bbbbbbbb gggggggg rrrrrrrr + else if(iThumbDisplayMode == EColor64K || iThumbDisplayMode == EColor4K) + bytesPerPixel = 2; // 12-bit rgb takes 2 bytes, stored as ggggbbbb xxxxrrrr + else + { + HandleThumbnailError(KErrNotSupported); + return; // support for 12-, 16- and 24-bit color images only + } + + // create output rgb buffer + TRAP(error, iRgbBuf = (TUint8*) User::AllocL(thumbLength * bytesPerPixel)); + if (HandleThumbnailError(error)) + return; + + TInt scanLineLength; + + // convert yuv to rgb + switch (iThumbDisplayMode) + { + + case EColor4K: + { + TInt error; + CYuv2Rgb12* yuvConverter; + TRAP(error, yuvConverter = new(ELeave) CYuv2Rgb12); + if (HandleThumbnailError(error)) + return; + scanLineLength = inputFrameResolution.iWidth * bytesPerPixel; + VPASSERT(yuvConverter); + TRAP(error, yuvConverter->ConstructL(inputFrameResolution.iWidth, inputFrameResolution.iHeight, inputFrameResolution.iWidth, inputFrameResolution.iHeight)); + if (HandleThumbnailError(error)) + return; + yuvConverter->Convert(yBuf, uBuf, vBuf, inputFrameResolution.iWidth, inputFrameResolution.iHeight, iRgbBuf, scanLineLength); + delete yuvConverter; + yuvConverter=0; + } + break; + + default: + case EColor64K: + { + TInt error; + CYuv2Rgb16* yuvConverter; + TRAP(error, yuvConverter = new(ELeave) CYuv2Rgb16); + if (HandleThumbnailError(error)) + return; + scanLineLength = inputFrameResolution.iWidth * bytesPerPixel; + VPASSERT(yuvConverter); + TRAP(error, yuvConverter->ConstructL(inputFrameResolution.iWidth, inputFrameResolution.iHeight, inputFrameResolution.iWidth, inputFrameResolution.iHeight);) + if (HandleThumbnailError(error)) + return; + yuvConverter->Convert(yBuf, uBuf, vBuf, inputFrameResolution.iWidth, inputFrameResolution.iHeight, iRgbBuf, scanLineLength); + delete yuvConverter; + yuvConverter=0; + } + break; + + case EColor16M: + { + TInt error; + CYuv2Rgb24* yuvConverter; + TRAP(error, yuvConverter = new(ELeave) CYuv2Rgb24); + if (HandleThumbnailError(error)) + return; + scanLineLength = inputFrameResolution.iWidth * bytesPerPixel; + VPASSERT(yuvConverter); + TRAP(error, yuvConverter->ConstructL(inputFrameResolution.iWidth, inputFrameResolution.iHeight, inputFrameResolution.iWidth, inputFrameResolution.iHeight)) + if (HandleThumbnailError(error)) + return; + yuvConverter->Convert(yBuf, uBuf, vBuf, inputFrameResolution.iWidth, inputFrameResolution.iHeight, iRgbBuf, scanLineLength); + delete yuvConverter; + yuvConverter=0; + } + break; + } + } + + if(!iThumbEnhance) + { + TSize inputFrameResolution(iParser->iStreamParameters.iVideoWidth,iParser->iStreamParameters.iVideoHeight); + + /* Pre-calculate pixel indices for horizontal scaling. */ + // inputFrameResolution is the resolution of the image read from video clip. + // iOutputThumbResolution is the final resolution desired by the caller. + + TInt xIncrement = inputFrameResolution.iWidth * iOutputThumbResolution.iWidth; + TInt xBoundary = iOutputThumbResolution.iWidth * iOutputThumbResolution.iWidth; + + TInt* xIndices = 0; + TRAPD(xIndicesErr, xIndices = new (ELeave) TInt[iOutputThumbResolution.iWidth]); + if (xIndicesErr == KErrNone) + { + TInt xDecision = xIncrement / bytesPerPixel; // looks like they changed here - orig was /2 + TInt sourceIndex = 0; + for (TInt x = 0; x < iOutputThumbResolution.iWidth; x++) + { + while (xDecision > xBoundary) + { + xDecision -= xBoundary; + sourceIndex += bytesPerPixel; + } + + xIndices[x] = sourceIndex; + xDecision += xIncrement; + } + } + else + { + HandleThumbnailError(xIndicesErr); + return; + } + + /* Initialize bitmap. */ + TRAPD(bitmapErr, iOutBitmap = new (ELeave) CFbsBitmap); + if ((xIndicesErr == KErrNone) && (bitmapErr == KErrNone)) + { + bitmapErr = iOutBitmap->Create(iOutputThumbResolution, iThumbDisplayMode/*EColor64K*/); + if (bitmapErr == KErrNone) + { + // Lock the heap to prevent the FBS server from invalidating the address + iOutBitmap->LockHeap(); + + /* Scale to desired iOutputThumbResolution and copy to bitmap. */ + TUint8* dataAddress = (TUint8*)iOutBitmap->DataAddress(); // fix + + TInt yIncrement = inputFrameResolution.iHeight * iOutputThumbResolution.iHeight; + TInt yBoundary = iOutputThumbResolution.iHeight * iOutputThumbResolution.iHeight; + + TInt targetIndex = 0; + TInt sourceRowIndex = 0; + TInt yDecision = yIncrement / 2; + for (TInt y = 0; y < iOutputThumbResolution.iHeight; y++) + { + while (yDecision > yBoundary) + { + yDecision -= yBoundary; + sourceRowIndex += (inputFrameResolution.iWidth * bytesPerPixel); + } + yDecision += yIncrement; + + + for (TInt x = 0; x < iOutputThumbResolution.iWidth; x++) + { + for (TInt i = 0; i < bytesPerPixel; ++i) + { + const TInt firstPixelSourceIndex = sourceRowIndex + xIndices[x] + i; + dataAddress[targetIndex] = iRgbBuf[firstPixelSourceIndex]; + targetIndex++; + } + } + } + iOutBitmap->UnlockHeap(); + } + + else + { + delete iOutBitmap; iOutBitmap = 0; + HandleThumbnailError(bitmapErr); + return; + } + } + else + { + HandleThumbnailError(bitmapErr); + delete[] xIndices; xIndices = 0; + return; + } + + delete[] xIndices; + xIndices = 0; + } + else // enhance + { + TInt i,j; + // create input bitmap and buffer + CFbsBitmap* inBitmap = 0; + TRAPD(inBitmapErr, inBitmap = new (ELeave) CFbsBitmap); + if( inBitmapErr == KErrNone ) + { + // create bitmaps + TSize originalResolution(iParser->iStreamParameters.iVideoWidth, iParser->iStreamParameters.iVideoHeight); + inBitmapErr = inBitmap->Create(originalResolution, iThumbDisplayMode/*EColor16M*/); + + if( inBitmapErr == KErrNone ) + { + // fill image from rgb buffer to input bitmap buffer + TPtr8 linePtr(0,0); + TInt lineLength = inBitmap->ScanLineLength(originalResolution.iWidth, iThumbDisplayMode); + for(j=0, i=0; jSetScanLine((TDes8&)linePtr,j); + } + + // create output bitmap + TRAPD(outBitmapErr, iOutBitmap = new (ELeave) CFbsBitmap); + if( outBitmapErr == KErrNone ) + { + outBitmapErr = iOutBitmap->Create(iOutputThumbResolution, iThumbDisplayMode/*EColor16M*/); // same size as input frame + + if( outBitmapErr == KErrNone ) + { + // post-processing enhancement + TRAP(outBitmapErr, EnhanceThumbnailL((const CFbsBitmap*)inBitmap, (CFbsBitmap*)iOutBitmap)); + + } + else + { + delete inBitmap; inBitmap = 0; + delete iOutBitmap; iOutBitmap = 0; + HandleThumbnailError(outBitmapErr); + return; + } + } + else + { + delete inBitmap; inBitmap = 0; + HandleThumbnailError(outBitmapErr); + return; + } + } + else + { + delete inBitmap; inBitmap = 0; + HandleThumbnailError(inBitmapErr); + return; + } + + // delete input bitmap + delete inBitmap; + inBitmap = 0; + } + else + { + HandleThumbnailError(inBitmapErr); + return; + } + } + + delete iRgbBuf; + iRgbBuf = 0; + + // Handle video decoder deletion. If the decoder has been used, + // it has to be reset before deletion + if (iDecoder) + { + iDecoder->StopL(); + + delete iDecoder; + iDecoder = 0; + } + + VPASSERT(iThumbnailRequestStatus); + User::RequestComplete(iThumbnailRequestStatus, KErrNone); + iThumbnailRequestStatus = 0; + + PRINT((_L("CTNEProcessorImpl::MMNotifyThumbnailReady() end"))) +} + + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::HandleThumbnailError +// Handle error in thumbnail generation +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CTNEProcessorImpl::HandleThumbnailError(TInt aError) +{ + if (aError != KErrNone) + { + VPASSERT(iThumbnailRequestStatus); + User::RequestComplete(iThumbnailRequestStatus, aError); + iThumbnailRequestStatus = 0; + return ETrue; + } + return EFalse; +} + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::FetchThumb +// Returns a pointer to completed thumbnail bitmap +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::FetchThumb(CFbsBitmap*& aThumb) +{ + aThumb = iOutBitmap; + iOutBitmap = 0; + + iState = EStateReadyToProcess; +} + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::InitializeClipL +// Initializes the processor for processing a clip +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::InitializeClipL(RFile& aFileHandle) +{ + + PRINT((_L("CTNEProcessorImpl::InitializeClipL() begin"))); + + iFirstFrameOfClip = ETrue; + iFirstFrameFlagSet = EFalse; + + if (!iParser) + { + iParser = (CMP4Parser*) CMP4Parser::NewL(this, aFileHandle); + } + + iParser->iFirstTimeClipParsing = ETrue; + iState = EStateIdle; + + // open file & parse header + CTNEProcessorImpl::TFileFormat format = CTNEProcessorImpl::EDataAutoDetect; + + User::LeaveIfError(OpenStream(aFileHandle, format)); + + if (iHaveVideo == EFalse) + User::Leave(KErrNotFound); + + VPASSERT(iState == EStateOpened); + + iState = EStatePreparing; + + // open demux & decoder + User::LeaveIfError(Prepare()); + + VPASSERT(iState == EStateReadyToProcess); + + PRINT((_L("CTNEProcessorImpl::InitializeClipL() end"))) +} + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::ParseHeaderOnlyL +// Parses the header for a given clip +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::ParseHeaderOnlyL(CParser::TStreamParameters& aStreamParams, RFile& aFileHandle) +{ + + if (!iParser) + { + // create an instance of the parser + iParser = (CMP4Parser*) CMP4Parser::NewL(this, aFileHandle); + } + iParser->ParseHeaderL(aStreamParams); + + // update output parameters. + UpdateStreamParameters(iParser->iStreamParameters, aStreamParams); +} + + + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::OpenStream +// Opens a clip for processing +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CTNEProcessorImpl::OpenStream(RFile& aFileHandle, TFileFormat aFileFormat) +{ + // We can only streams in idle state + if (iState != EStateIdle) + return EInvalidProcessorState; + + TInt error = KErrNone; + iFileFormat = aFileFormat; + + // set descriptor to read buffer + TPtr8 readDes(0,0); + readDes.Set(iReadBuf, 0, KReadBufInitSize); + + // read data from the file + if ( (error = aFileHandle.Read(readDes)) != KErrNone ) + return error; + + if ( readDes.Length() < 8 ) + return KErrGeneral; + + + // detect if format is 3GP, 5-8 == "ftyp" + // This method is not 100 % proof, but good enough + if ( (iReadBuf[4] == 0x66) && (iReadBuf[5] == 0x74) && + (iReadBuf[6] == 0x79) && (iReadBuf[7] == 0x70) ) + { + iFileFormat = EData3GP; + iMuxType = EMux3GP; + } + else + return KErrNotSupported; + + // parse 3GP header + CMP4Parser *parser = 0; + if ( !iParser ) + { + TRAP(error, (parser = CMP4Parser::NewL(this, aFileHandle)) ); + if (error != KErrNone) + return error; + iParser = parser; + } + else + parser = (CMP4Parser*)iParser; + + TRAP(error, ParseHeaderL()); + + if (error != KErrNone) + return error; + + iState = EStateOpened; + + return KErrNone; +} + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::CloseStream +// Closes the processed stream from parser +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CTNEProcessorImpl::CloseStream() +{ + + PRINT((_L("CTNEProcessorImpl::CloseStream() begin - iState = %d"), iState)) + + if ( (iState != EStateOpened) && (iState != EStateProcessing) ) + return EInvalidProcessorState; + + TInt error=0; + + // delete parser + if (iParser) + { + TRAP(error, + { + delete iParser; + iParser=0; + } + ); + if (error != KErrNone) + return error; + } + + // We are idle again + iState = EStateIdle; + + PRINT((_L("CTNEProcessorImpl::CloseStream() end "))) + + return KErrNone; +} + + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::Prepare +// Prepares the processor for processing, opens demux & decoder +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CTNEProcessorImpl::Prepare() +{ + // We can only prepare from preparing state + if (iState != EStatePreparing) + return EInvalidProcessorState; + + // Make sure we now know the stream format + if (iFileFormat == EDataAutoDetect) + return EUnsupportedFormat; + + // Check whether the stream has audio, video or both, and whether it is + // muxed + switch (iFileFormat) + { + case EData3GP: + // the video and audio flags are set when + // the header is parsed. + iIsMuxed = ETrue; + break; + default: + User::Panic(_L("CTNEProcessorImpl"), EInvalidInternalState); + } + + iState = EStateReadyToProcess; + + return KErrNone; +} + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::DoCloseVideoL +// Closes & deletes the structures used in processing +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::DoCloseVideoL() +{ + if ((iState == EStateProcessing) || (iState == EStateReadyToProcess)|| + (iState == EStatePreparing) ) + { + PRINT((_L("CTNEProcessorImpl::DoCloseVideoL() - stopping"))) + User::LeaveIfError(Stop()); + iState = EStateOpened; + } + + // If we are buffering or opening at the moment or clip is open then close it + if ( (iState == EStateOpened) || (iState == EStateReadyToProcess)) + { + PRINT((_L("CTNEProcessorImpl::DoCloseVideoL() - closing stream"))) + User::LeaveIfError(CloseStream()); + iState = EStateIdle; + } +} + + + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::Stop +// Stops processing & closes modules used in processing +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CTNEProcessorImpl::Stop() +{ + iDecoding = EFalse; + + // Check state + if ( (iState != EStateProcessing) && (iState != EStateReadyToProcess) && (iState != EStatePreparing) ) + return EInvalidProcessorState; + // We may also get here from the middle of a Prepare() attempt. + + PRINT((_L("CTNEProcessorImpl::Stop() begin"))) + + // Handle video encoder deletion. If the encoder has been used, + // it has to be reseted before deleting + if (iDecoder) + { + iDecoder->StopL(); + + delete iDecoder; + iDecoder = 0; + } + + iState = EStateOpened; + + PRINT((_L("CTNEProcessorImpl::Stop() end"))) + + return KErrNone; +} + + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::Close +// Stops processing and closes all submodules except status monitor +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CTNEProcessorImpl::Close() +{ + + // delete all objects except status monitor + TRAPD(error, DoCloseVideoL()); + if (error != KErrNone) + return error; + + iState = EStateIdle; + + return KErrNone; + +} + + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::RunL +// Called by the active scheduler when the video encoder initialization is done +// or an ending black frame has been encoded +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::RunL() +{ + + PRINT((_L("CancelProcessingL begin, iDecoding %d, iDecoderInitPending %d, iDecodePending %d"), + iDecoding, iDecoderInitPending, iDecodePending )); + + // @@ YHK we probably dont need this flag ??? + // Don't decode if we aren't decoding + if (!iDecoding) + { + if (!IsActive()) + { + SetActive(); + iStatus = KRequestPending; + } + PRINT((_L("CH263Decoder::RunL() out from !iDecoding branch"))) + return; + } + + if (iDecoderInitPending) + { + iDecoderInitPending = EFalse; + if (iStatus != KErrNone) + { + MNotifyThumbnailReady(iStatus.Int()); + return; + } + // at this point we have already read a frame, + // so now start processing + iDecoder->StartL(); + + // stop if a fatal error has occurred in starting + // the transcoder (decoding stopped in MtroFatalError) + if (!iDecoding) + return; + + DecodeThumb(ETrue); + return; + } + + if (iDecodePending) + { + iDecodePending = EFalse; + + DecodeThumb(EFalse); + return; + } +} + + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::RunError +// Called by the AO framework when RunL method has leaved +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CTNEProcessorImpl::RunError(TInt aError) +{ + return aError; +} + + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::DoCancel +// Cancels any pending asynchronous requests +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::DoCancel() +{ + + PRINT((_L("CTNEProcessorImpl::DoCancel() begin"))) + + // Cancel our internal request + if ( iStatus == KRequestPending ) + { + PRINT((_L("CTNEProcessorImpl::DoCancel() cancel request"))) + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrCancel); + } + + PRINT((_L("CTNEProcessorImpl::DoCancel() end"))) +} + + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::SetHeaderDefaults +// Sets appropriate default values for processing parameters +// in audio-only case +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::SetHeaderDefaults() +{ + + // set suitable default values + iHaveVideo = ETrue; + iVideoType = EVideoH263Profile0Level10; + + iVideoParameters.iWidth = 0; + iVideoParameters.iHeight = 0; + iVideoParameters.iIntraFrequency = 0; + iVideoParameters.iNumScalabilityLayers = 0; + iVideoParameters.iReferencePicturesNeeded = 0; + // picture period in nanoseconds + iVideoParameters.iPicturePeriodNsec = TInt64(33366667); + + iStreamLength = 0; + iStreamSize = 0; + iStreamBitrate = 10000; + +} + + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::ParseHeaderL +// Parses the clip header & sets internal variables accordingly +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::ParseHeaderL() +{ + VPASSERT(iParser); + + if ( iMuxType != EMux3GP ) + User::Leave(EUnsupportedFormat); + + CParser::TStreamParameters streamParams; + + // parse + iParser->ParseHeaderL(streamParams); + + // copy input stream info into parser + UpdateStreamParameters(iParser->iStreamParameters, streamParams); + + // copy parameters + iHaveVideo = streamParams.iHaveVideo; + iVideoType = (TVideoType)streamParams.iVideoFormat; + iCanSeek = streamParams.iCanSeek; + iVideoParameters.iWidth = streamParams.iVideoWidth; + iVideoParameters.iHeight = streamParams.iVideoHeight; + iVideoParameters.iIntraFrequency = streamParams.iVideoIntraFrequency; + iVideoParameters.iNumScalabilityLayers = streamParams.iNumScalabilityLayers; + iVideoParameters.iReferencePicturesNeeded = streamParams.iReferencePicturesNeeded; + iVideoParameters.iPicturePeriodNsec = streamParams.iVideoPicturePeriodNsec; + + iStreamLength = streamParams.iStreamLength; + iStreamBitrate = streamParams.iStreamBitrate; + iStreamSize = streamParams.iStreamSize; + + // Ensure that the video isn't too large + if ( (iVideoParameters.iWidth > KTNEMaxVideoWidth) || + (iVideoParameters.iHeight > KTNEMaxVideoHeight) ) + User::Leave(EVideoTooLarge); + +} + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::UpdateStreamParameters +// Copies stream parameters to destination structure +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::UpdateStreamParameters(CParser::TStreamParameters& aDestParameters, + CParser::TStreamParameters& aSrcParameters) +{ + aDestParameters.iHaveVideo = aSrcParameters.iHaveVideo; + aDestParameters.iVideoFormat = aSrcParameters.iVideoFormat; + aDestParameters.iVideoWidth = aSrcParameters.iVideoWidth; + aDestParameters.iVideoHeight = aSrcParameters.iVideoHeight; + aDestParameters.iVideoPicturePeriodNsec = aSrcParameters.iVideoPicturePeriodNsec; + aDestParameters.iVideoIntraFrequency = aSrcParameters.iVideoIntraFrequency; + aDestParameters.iStreamLength = aSrcParameters.iStreamLength; + aDestParameters.iVideoLength = aSrcParameters.iVideoLength; + aDestParameters.iCanSeek = aSrcParameters.iCanSeek; + aDestParameters.iStreamSize = aSrcParameters.iStreamSize; + aDestParameters.iStreamBitrate = aSrcParameters.iStreamBitrate; + aDestParameters.iMaxPacketSize = aSrcParameters.iMaxPacketSize; + aDestParameters.iLogicalChannelNumberVideo = aSrcParameters.iLogicalChannelNumberVideo; + aDestParameters.iReferencePicturesNeeded = aSrcParameters.iReferencePicturesNeeded; + + aDestParameters.iFrameRate = aSrcParameters.iFrameRate; + +} + + +// ----------------------------------------------------------------------------- +// CTNEProcessorImpl::EnhanceThumbnailL +// Enhances the visual quality of the frame +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CTNEProcessorImpl::EnhanceThumbnailL(const CFbsBitmap* aInBitmap, + CFbsBitmap* aTargetBitmap) +{ + + // create enhancement object + if(!iEnhancer) + iEnhancer = (CDisplayChain*) CDisplayChain::NewL(); + + // enhance image + iEnhancer->ProcessL(aInBitmap, aTargetBitmap); + + // clear enhancement object + delete iEnhancer; + iEnhancer=0; + +} + +// --------------------------------------------------------- +// CH263Decoder::CheckFrameQuality +// Checks if a frame has "good" or "legible" quality +// (other items were commented in a header). +// --------------------------------------------------------- +// +TInt CTNEProcessorImpl::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 = iVideoParameters.iWidth*iVideoParameters.iHeight; + + // gather image statistics + for(i=0, numberOfSamples=0; i maxValue) + maxValue = *aYUVDataPtr; + if(*aYUVDataPtr < minValue) + minValue = *aYUVDataPtr; + } + //VDASSERT(numberOfSamples,10); + averageValue = runningSum/numberOfSamples; + + // make decision based statistics + if((maxValue - minValue) < minMaxDeltaThreshold) + goodFrame = 0; + else + { + if(averageValue < (minValue + extremeRegionThreshold) || + averageValue > (maxValue - extremeRegionThreshold)) + goodFrame = 0; + } + return goodFrame; +} + +// ----------------------------------------------------------------------------- +// CVideoProcessorImpl::GetVideoClipFrameRate +// Gets video frame rate of current clip +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TReal CTNEProcessorImpl::GetVideoClipFrameRate() + { + + TReal rate; + iParser->GetVideoFrameRate(rate); + + return rate; + + } + +// ----------------------------------------------------------------------------- +// CVideoProcessorImpl::GetVideoTimeInMsFromTicks +// Converts a video timestamp from ticks to milliseconds +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt64 CTNEProcessorImpl::GetVideoTimeInMsFromTicks(TInt64 aTimeStampInTicks, TBool /*aCommonTimeScale*/) const +{ + // @@ YHK code modified check this if this is necessary + TUint timeScale = iParser->iStreamParameters.iVideoTimeScale; + VPASSERT(timeScale > 0); + return TInt64( I64REAL(aTimeStampInTicks) / (TReal)timeScale * 1000 + 0.5 ); +} + +//============================================================================= + + +// End of File +